From 899ba579fc55a04e95b780ffec5d20d0942dbda2 Mon Sep 17 00:00:00 2001 From: William Yang Date: Sat, 27 Mar 2021 14:03:10 +0100 Subject: [PATCH 001/174] feat(quic): compile and start quicer listener. --- apps/emqx/etc/emqx.conf | 306 +++++++++++++++++++++++++++++++ apps/emqx/src/emqx_listeners.erl | 17 +- rebar.config | 1 + rebar.config.erl | 1 + 4 files changed, 324 insertions(+), 1 deletion(-) diff --git a/apps/emqx/etc/emqx.conf b/apps/emqx/etc/emqx.conf index f6627bf1c..6be808264 100644 --- a/apps/emqx/etc/emqx.conf +++ b/apps/emqx/etc/emqx.conf @@ -2155,6 +2155,312 @@ listener.wss.external.allow_origin_absence = true ## Value: http://url eg. https://localhost:8084, https://127.0.0.1:8084 listener.wss.external.check_origins = "https://localhost:8084, https://127.0.0.1:8084" + +##-------------------------------------------------------------------- +## External QUIC listener for MQTT Protocol + +## listener.quic.$name is the IP address and port that the MQTT/QUIC +## listener will bind. +## +## Value: IP:Port | Port +## +## Examples: 8084, 127.0.0.1:8084, ::1:8084 +listener.quic.external = 4567 + +## The path of WebSocket MQTT endpoint +## +## Value: URL Path +listener.quic.external.mqtt_path = /mqtt + +## The acceptor pool for external MQTT/QUIC listener. +## +## Value: Number +listener.quic.external.acceptors = 4 + +## Maximum number of concurrent MQTT/Webwocket/SSL connections. +## +## Value: Number +listener.quic.external.max_connections = 16 + +## Maximum MQTT/QUIC connections per second. +## +## See: listener.tcp.$name.max_conn_rate +## +## Value: Number +listener.quic.external.max_conn_rate = 1000 + +## Simulate the {active, N} option for the MQTT/QUIC connections. +## +## Value: Number +listener.quic.external.active_n = 100 + +## Zone of the external MQTT/QUIC listener belonged to. +## +## Value: String +listener.quic.external.zone = external + +## The access control rules for the MQTT/QUIC listener. +## +## See: listener.tcp.$name.access. +## +## Value: ACL Rule +listener.quic.external.access.1 = allow all + +## If set to true, the server fails if the client does not have a Sec-WebSocket-Protocol to send. +## Set to false for WeChat MiniApp. +## +## Value: true | false +## listener.quic.external.fail_if_no_subprotocol = true + +## Supported subprotocols +## +## Default: mqtt, mqtt-v3, mqtt-v3.1.1, mqtt-v5 +## listener.quic.external.supported_subprotocols = mqtt, mqtt-v3, mqtt-v3.1.1, mqtt-v5 + +## Enable the Proxy Protocol V1/2 support. +## +## See: listener.tcp.$name.proxy_protocol +## +## Value: on | off +## listener.quic.external.proxy_protocol = on + +## Sets the timeout for proxy protocol. +## +## See: listener.tcp.$name.proxy_protocol_timeout +## +## Value: Duration +## listener.quic.external.proxy_protocol_timeout = 3s + +## TLS versions only to protect from POODLE attack. +## +## See: listener.ssl.$name.tls_versions +## +## Value: String, seperated by ',' +## NOTE: Do not use tlsv1.3 if emqx is running on OTP-22 or earlier +## listener.quic.external.tls_versions = tlsv1.3,tlsv1.2,tlsv1.1,tlsv1 + +## Path to the file containing the user's private PEM-encoded key. +## +## See: listener.ssl.$name.keyfile +## +## Value: File +listener.quic.external.keyfile = {{ platform_etc_dir }}/certs/key.pem + +## Path to a file containing the user certificate. +## +## See: listener.ssl.$name.certfile +## +## Value: File +listener.quic.external.certfile = {{ platform_etc_dir }}/certs/cert.pem + +## Path to the file containing PEM-encoded CA certificates. +## +## See: listener.ssl.$name.cacert +## +## Value: File +## listener.quic.external.cacertfile = {{ platform_etc_dir }}/certs/cacert.pem + +## Maximum number of non-self-issued intermediate certificates that +## can follow the peer certificate in a valid certification path. +## +## See: listener.ssl.external.depth +## +## Value: Number +## listener.quic.external.depth = 10 + +## String containing the user's password. Only used if the private keyfile +## is password-protected. +## +## See: listener.ssl.$name.key_password +## +## Value: String +## listener.quic.external.key_password = yourpass + +## See: listener.ssl.$name.dhfile +## +## Value: File +## listener.ssl.external.dhfile = {{ platform_etc_dir }}/certs/dh-params.pem + +## See: listener.ssl.$name.verify +## +## Value: verify_peer | verify_none +## listener.quic.external.verify = verify_peer + +## See: listener.ssl.$name.fail_if_no_peer_cert +## +## Value: false | true +## listener.quic.external.fail_if_no_peer_cert = true + +## See: listener.ssl.$name.ciphers +## +## Value: Ciphers +listener.quic.external.ciphers = TLS_AES_256_GCM_SHA384,TLS_AES_128_GCM_SHA256,TLS_CHACHA20_POLY1305_SHA256,TLS_AES_128_CCM_SHA256,TLS_AES_128_CCM_8_SHA256,ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384,ECDHE-ECDSA-AES256-SHA384,ECDHE-RSA-AES256-SHA384,ECDHE-ECDSA-DES-CBC3-SHA,ECDH-ECDSA-AES256-GCM-SHA384,ECDH-RSA-AES256-GCM-SHA384,ECDH-ECDSA-AES256-SHA384,ECDH-RSA-AES256-SHA384,DHE-DSS-AES256-GCM-SHA384,DHE-DSS-AES256-SHA256,AES256-GCM-SHA384,AES256-SHA256,ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-RSA-AES128-GCM-SHA256,ECDHE-ECDSA-AES128-SHA256,ECDHE-RSA-AES128-SHA256,ECDH-ECDSA-AES128-GCM-SHA256,ECDH-RSA-AES128-GCM-SHA256,ECDH-ECDSA-AES128-SHA256,ECDH-RSA-AES128-SHA256,DHE-DSS-AES128-GCM-SHA256,DHE-DSS-AES128-SHA256,AES128-GCM-SHA256,AES128-SHA256,ECDHE-ECDSA-AES256-SHA,ECDHE-RSA-AES256-SHA,DHE-DSS-AES256-SHA,ECDH-ECDSA-AES256-SHA,ECDH-RSA-AES256-SHA,AES256-SHA,ECDHE-ECDSA-AES128-SHA,ECDHE-RSA-AES128-SHA,DHE-DSS-AES128-SHA,ECDH-ECDSA-AES128-SHA,ECDH-RSA-AES128-SHA,AES128-SHA + +## Ciphers for TLS PSK. +## Note that 'listener.quic.external.ciphers' and 'listener.quic.external.psk_ciphers' cannot +## be configured at the same time. +## See 'https://tools.ietf.org/html/rfc4279#section-2'. +## listener.quic.external.psk_ciphers = PSK-AES128-CBC-SHA,PSK-AES256-CBC-SHA,PSK-3DES-EDE-CBC-SHA,PSK-RC4-SHA + +## See: listener.ssl.$name.secure_renegotiate +## +## Value: on | off +## listener.quic.external.secure_renegotiate = off + +## See: listener.ssl.$name.reuse_sessions +## +## Value: on | off +## listener.quic.external.reuse_sessions = on + +## See: listener.ssl.$name.honor_cipher_order +## +## Value: on | off +## listener.quic.external.honor_cipher_order = on + +## See: listener.ssl.$name.peer_cert_as_username +## +## Value: cn | dn | crt | pem | md5 +## listener.quic.external.peer_cert_as_username = cn + +## See: listener.ssl.$name.peer_cert_as_clientid +## +## Value: cn | dn | crt | pem | md5 +## listener.quic.external.peer_cert_as_clientid = cn + +## TCP backlog for the QUIC connection. +## +## See: listener.tcp.$name.backlog +## +## Value: Number >= 0 +listener.quic.external.backlog = 1024 + +## The TCP send timeout for the QUIC connection. +## +## See: listener.tcp.$name.send_timeout +## +## Value: Duration +listener.quic.external.send_timeout = 15s + +## Close the QUIC connection if send timeout. +## +## See: listener.tcp.$name.send_timeout_close +## +## Value: on | off +listener.quic.external.send_timeout_close = on + +## The TCP receive buffer(os kernel) for the QUIC connections. +## +## See: listener.tcp.$name.recbuf +## +## Value: Bytes +## listener.quic.external.recbuf = 4KB + +## The TCP send buffer(os kernel) for the QUIC connections. +## +## See: listener.tcp.$name.sndbuf +## +## Value: Bytes +## listener.quic.external.sndbuf = 4KB + +## The size of the user-level software buffer used by the driver. +## +## See: listener.tcp.$name.buffer +## +## Value: Bytes +## listener.quic.external.buffer = 4KB + +## The TCP_NODELAY flag for QUIC connections. +## +## See: listener.tcp.$name.nodelay +## +## Value: true | false +## listener.quic.external.nodelay = true + +## The compress flag for external QUIC connections. +## +## If this Value is set true,the websocket message would be compressed +## +## Value: true | false +## listener.quic.external.compress = true + +## The level of deflate options for external QUIC connections. +## +## See: listener.quic.$name.deflate_opts.level +## +## Value: none | default | best_compression | best_speed +## listener.quic.external.deflate_opts.level = default + +## The mem_level of deflate options for external QUIC connections. +## +## See: listener.quic.$name.deflate_opts.mem_level +## +## Valid range is 1-9 +## listener.quic.external.deflate_opts.mem_level = 8 + +## The strategy of deflate options for external QUIC connections. +## +## See: listener.quic.$name.deflate_opts.strategy +## +## Value: default | filtered | huffman_only | rle +## listener.quic.external.deflate_opts.strategy = default + +## The deflate option for external QUIC connections. +## +## See: listener.quic.$name.deflate_opts.server_context_takeover +## +## Value: takeover | no_takeover +## listener.quic.external.deflate_opts.server_context_takeover = takeover + +## The deflate option for external QUIC connections. +## +## See: listener.quic.$name.deflate_opts.client_context_takeover +## +## Value: takeover | no_takeover +## listener.quic.external.deflate_opts.client_context_takeover = takeover + +## The deflate options for external QUIC connections. +## +## See: listener.quic.$name.deflate_opts.server_max_window_bits +## +## Valid range is 8-15 +## listener.quic.external.deflate_opts.server_max_window_bits = 15 + +## The deflate options for external QUIC connections. +## +## See: listener.quic.$name.deflate_opts.client_max_window_bits +## +## Valid range is 8-15 +## listener.quic.external.deflate_opts.client_max_window_bits = 15 + +## The idle timeout for external QUIC connections. +## +## See: listener.quic.$name.idle_timeout +## +## Value: Duration +## listener.quic.external.idle_timeout = 60s + +## The max frame size for external QUIC connections. +## +## Value: Number +## listener.quic.external.max_frame_size = 0 + +## Whether a WebSocket message is allowed to contain multiple MQTT packets +## +## Value: single | multiple +listener.quic.external.mqtt_piggyback = multiple +## Enable origin check in header for secure websocket connection +## +## Value: true | false (default false) +listener.quic.external.check_origin_enable = false +## Allow origin to be absent in header in secure websocket connection when check_origin_enable is true +## +## Value: true | false (default true) +listener.quic.external.allow_origin_absence = true +## Comma separated list of allowed origin in header for secure websocket connection +## +## Value: http://url eg. https://localhost:8084, https://127.0.0.1:8084 +listener.quic.external.check_origins = https://localhost:8084, https://127.0.0.1:8084 + ## CONFIG_SECTION_END=listeners ================================================ ## CONFIG_SECTION_BGN=modules ================================================== diff --git a/apps/emqx/src/emqx_listeners.erl b/apps/emqx/src/emqx_listeners.erl index 1f3d1776b..d97fe32ed 100644 --- a/apps/emqx/src/emqx_listeners.erl +++ b/apps/emqx/src/emqx_listeners.erl @@ -139,7 +139,22 @@ start_listener(Proto, ListenOn, Options) when Proto == http; Proto == ws -> %% Start MQTT/WSS listener start_listener(Proto, ListenOn, Options) when Proto == https; Proto == wss -> start_http_listener(fun cowboy:start_tls/3, 'mqtt:wss', ListenOn, - ranch_opts(Options), ws_opts(Options)). + ranch_opts(Options), ws_opts(Options)); + +%% MQTT over QUIC +start_listener(quic, ListenOn, Options) -> + SSLOpts = proplists:get_value(ssl_options, Options), + ListenOpts = [ {cert, proplists:get_value(certfile, SSLOpts)} + , {key, proplists:get_value(keyfile, SSLOpts)} + , {alpn, ["mqtt"]} + , {conn_acceptors, 32} + ], + ConnectionOpts = [ {conn_callback, quicer_server_conn_callback} + , {idle_timeout_ms, 5000} + , {peer_unidi_stream_count, 1} + , {peer_bidi_stream_count, 10}], + StreamOpts = [{stream_callback, quicer_echo_server_stream_callback}], + quicer:start_listener('mqtt:quic', ListenOn, {ListenOpts, ConnectionOpts, StreamOpts}). replace(Opts, Key, Value) -> [{Key, Value} | proplists:delete(Key, Opts)]. diff --git a/rebar.config b/rebar.config index 109b680c5..eaa0ea6cf 100644 --- a/rebar.config +++ b/rebar.config @@ -54,6 +54,7 @@ , {snabbkaffe, {git, "https://github.com/kafka4beam/snabbkaffe.git", {tag, "0.13.0"}}} , {hocon, {git, "https://github.com/emqx/hocon.git", {tag, "0.5.1"}}} , {emqx_http_lib, {git, "https://github.com/emqx/emqx_http_lib.git", {tag, "0.2.1"}}} + , {quicer, {git, "https://github.com/qzhuyan/quic.git", {branch, "quicer_application"}}} ]}. {xref_ignores, diff --git a/rebar.config.erl b/rebar.config.erl index 0248e6dab..a05b09686 100644 --- a/rebar.config.erl +++ b/rebar.config.erl @@ -241,6 +241,7 @@ relx_apps(ReleaseType) -> , compiler , runtime_tools , cuttlefish + , quicer , emqx , {mnesia, load} , {ekka, load} From 70f22d2c1b980e885f96354fea35dae57fbd75ee Mon Sep 17 00:00:00 2001 From: William Yang Date: Tue, 30 Mar 2021 23:39:09 +0200 Subject: [PATCH 002/174] feat(quic): reuse emqx_connection module for quic. --- apps/emqx/src/emqx_connection.erl | 7 +++ apps/emqx/src/emqx_listeners.erl | 4 +- apps/emqx/src/emqx_quic_connection.erl | 26 ++++++++ apps/emqx/src/emqx_quic_stream.erl | 83 ++++++++++++++++++++++++++ 4 files changed, 118 insertions(+), 2 deletions(-) create mode 100644 apps/emqx/src/emqx_quic_connection.erl create mode 100644 apps/emqx/src/emqx_quic_stream.erl diff --git a/apps/emqx/src/emqx_connection.erl b/apps/emqx/src/emqx_connection.erl index ab91c02b4..6900e4f1e 100644 --- a/apps/emqx/src/emqx_connection.erl +++ b/apps/emqx/src/emqx_connection.erl @@ -416,6 +416,13 @@ handle_msg({Inet, _Sock, Data}, State) when Inet == tcp; Inet == ssl -> ok = emqx_metrics:inc('bytes.received', Oct), parse_incoming(Data, State); +handle_msg({quic, Data, _Sock, _, _, _}, State) -> + ?LOG(debug, "RECV ~0p", [Data]), + Oct = iolist_size(Data), + inc_counter(incoming_bytes, Oct), + ok = emqx_metrics:inc('bytes.received', Oct), + parse_incoming(Data, State); + handle_msg({incoming, Packet = ?CONNECT_PACKET(ConnPkt)}, State = #state{idle_timer = IdleTimer}) -> ok = emqx_misc:cancel_timer(IdleTimer), diff --git a/apps/emqx/src/emqx_listeners.erl b/apps/emqx/src/emqx_listeners.erl index d97fe32ed..b3d6bf319 100644 --- a/apps/emqx/src/emqx_listeners.erl +++ b/apps/emqx/src/emqx_listeners.erl @@ -149,11 +149,11 @@ start_listener(quic, ListenOn, Options) -> , {alpn, ["mqtt"]} , {conn_acceptors, 32} ], - ConnectionOpts = [ {conn_callback, quicer_server_conn_callback} + ConnectionOpts = [ {conn_callback, emqx_quic_connection} , {idle_timeout_ms, 5000} , {peer_unidi_stream_count, 1} , {peer_bidi_stream_count, 10}], - StreamOpts = [{stream_callback, quicer_echo_server_stream_callback}], + StreamOpts = [], quicer:start_listener('mqtt:quic', ListenOn, {ListenOpts, ConnectionOpts, StreamOpts}). replace(Opts, Key, Value) -> [{Key, Value} | proplists:delete(Key, Opts)]. diff --git a/apps/emqx/src/emqx_quic_connection.erl b/apps/emqx/src/emqx_quic_connection.erl new file mode 100644 index 000000000..b83522c6e --- /dev/null +++ b/apps/emqx/src/emqx_quic_connection.erl @@ -0,0 +1,26 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2021 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_quic_connection). + +%% Callbacks +-export([ new_conn/2 + ]). + +new_conn(Conn, {_L, COpts, _S}) when is_map(COpts) -> + new_conn(Conn, maps:to_list(COpts)); +new_conn(Conn, COpts) -> + emqx_connection:start_link(emqx_quic_stream, Conn, COpts). diff --git a/apps/emqx/src/emqx_quic_stream.erl b/apps/emqx/src/emqx_quic_stream.erl new file mode 100644 index 000000000..e12d95f30 --- /dev/null +++ b/apps/emqx/src/emqx_quic_stream.erl @@ -0,0 +1,83 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2021 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +%% MQTT/QUIC Stream +-module(emqx_quic_stream). + +%% emqx transport Callbacks +-export([ type/1 + , wait/1 + , getstat/2 + , fast_close/1 + , ensure_ok_or_exit/2 + , async_send/3 + , setopts/2 + , getopts/2 + , peername/1 + , sockname/1 + , peercert/1 + ]). + +wait(Conn) -> + quicer:accept_stream(Conn, []). + +type(_) -> + quic. + +peername(S) -> + quicer:peername(S). + +sockname(S) -> + quicer:sockname(S). + +peercert(_S) -> + nossl. + +getstat(Socket, Stats) -> + Res = quicer:getstats(Socket, Stats), + {ok, lists:keyreplace(send_pend, 1, Res, {send_pend, 0})}. + +setopts(_Socket, _Opts) -> + ok. + +getopts(_Socket, _Opts) -> + %% todo + { ok, [{high_watermark, 0}, + {high_msgq_watermark, 0}, + {sndbuf, 0}, + {recbuf, 0}, + {buffer,80000}]}. + +fast_close(Stream) -> + quicer:close_stream(Stream). + +-spec(ensure_ok_or_exit(atom(), list(term())) -> term()). +ensure_ok_or_exit(Fun, Args = [Sock|_]) when is_atom(Fun), is_list(Args) -> + case erlang:apply(?MODULE, Fun, Args) of + {error, Reason} when Reason =:= enotconn; Reason =:= closed -> + fast_close(Sock), + exit(normal); + {error, Reason} -> + fast_close(Sock), + exit({shutdown, Reason}); + Result -> Result + end. + +async_send(Stream, Data, Options) when is_list(Data) -> + async_send(Stream, iolist_to_binary(Data), Options); +async_send(Stream, Data, _Options) when is_binary(Data) -> + {ok, _Len} = quicer:send(Stream, Data), + ok. From 087aa1dd53bb87f45b997e6b07e9caecb73f252c Mon Sep 17 00:00:00 2001 From: William Yang Date: Wed, 31 Mar 2021 13:45:40 +0200 Subject: [PATCH 003/174] feat(quic): handle stream close. --- apps/emqx/etc/emqx.conf | 4 ++-- apps/emqx/src/emqx_connection.erl | 3 +++ apps/emqx/src/emqx_quic_stream.erl | 4 +++- rebar.config | 2 +- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/apps/emqx/etc/emqx.conf b/apps/emqx/etc/emqx.conf index 6be808264..19100c41e 100644 --- a/apps/emqx/etc/emqx.conf +++ b/apps/emqx/etc/emqx.conf @@ -2164,8 +2164,8 @@ listener.wss.external.check_origins = "https://localhost:8084, https://127.0.0.1 ## ## Value: IP:Port | Port ## -## Examples: 8084, 127.0.0.1:8084, ::1:8084 -listener.quic.external = 4567 +## Examples: 14567, 127.0.0.1:14567, ::1:14567 +listener.quic.external = 14567 ## The path of WebSocket MQTT endpoint ## diff --git a/apps/emqx/src/emqx_connection.erl b/apps/emqx/src/emqx_connection.erl index 6900e4f1e..8e3ee400b 100644 --- a/apps/emqx/src/emqx_connection.erl +++ b/apps/emqx/src/emqx_connection.erl @@ -738,6 +738,9 @@ handle_info({sock_error, Reason}, State) -> end, handle_info({sock_closed, Reason}, close_socket(State)); +handle_info({quic, closed, _Channel, ReasonFlag}, State) -> + handle_info({sock_closed, ReasonFlag}, State); + handle_info(Info, State) -> with_channel(handle_info, [Info], State). diff --git a/apps/emqx/src/emqx_quic_stream.erl b/apps/emqx/src/emqx_quic_stream.erl index e12d95f30..a80af643a 100644 --- a/apps/emqx/src/emqx_quic_stream.erl +++ b/apps/emqx/src/emqx_quic_stream.erl @@ -62,7 +62,9 @@ getopts(_Socket, _Opts) -> {buffer,80000}]}. fast_close(Stream) -> - quicer:close_stream(Stream). + quicer:close_stream(Stream), + %% Stream might be closed already. + ok. -spec(ensure_ok_or_exit(atom(), list(term())) -> term()). ensure_ok_or_exit(Fun, Args = [Sock|_]) when is_atom(Fun), is_list(Args) -> diff --git a/rebar.config b/rebar.config index eaa0ea6cf..ac0604728 100644 --- a/rebar.config +++ b/rebar.config @@ -54,7 +54,7 @@ , {snabbkaffe, {git, "https://github.com/kafka4beam/snabbkaffe.git", {tag, "0.13.0"}}} , {hocon, {git, "https://github.com/emqx/hocon.git", {tag, "0.5.1"}}} , {emqx_http_lib, {git, "https://github.com/emqx/emqx_http_lib.git", {tag, "0.2.1"}}} - , {quicer, {git, "https://github.com/qzhuyan/quic.git", {branch, "quicer_application"}}} + , {quicer, {git, "https://github.com/qzhuyan/quic.git", {branch, "fix/getopt3-free-bin"}}} ]}. {xref_ignores, From 570e096b5690ece5aba7ad01304b589b8909f639 Mon Sep 17 00:00:00 2001 From: William Yang Date: Sat, 3 Apr 2021 11:22:47 +0200 Subject: [PATCH 004/174] fix(quic): return empty list for dead 'Socket' --- apps/emqx/src/emqx_quic_stream.erl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/emqx/src/emqx_quic_stream.erl b/apps/emqx/src/emqx_quic_stream.erl index a80af643a..1c99ad7da 100644 --- a/apps/emqx/src/emqx_quic_stream.erl +++ b/apps/emqx/src/emqx_quic_stream.erl @@ -47,8 +47,10 @@ peercert(_S) -> nossl. getstat(Socket, Stats) -> - Res = quicer:getstats(Socket, Stats), - {ok, lists:keyreplace(send_pend, 1, Res, {send_pend, 0})}. + case quicer:getstats(Socket, Stats) of + {error, _} -> []; + Res -> {ok, Res} + end. setopts(_Socket, _Opts) -> ok. From 9570d01792fbdd91301dddf511037a9d8e1dcc83 Mon Sep 17 00:00:00 2001 From: William Yang Date: Mon, 5 Apr 2021 14:28:00 +0200 Subject: [PATCH 005/174] fix(quic): error handling for getstats. - return {error, closed} instead - quicer demo/3 branch. --- apps/emqx/src/emqx_quic_stream.erl | 2 +- rebar.config | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/emqx/src/emqx_quic_stream.erl b/apps/emqx/src/emqx_quic_stream.erl index 1c99ad7da..056c5dd24 100644 --- a/apps/emqx/src/emqx_quic_stream.erl +++ b/apps/emqx/src/emqx_quic_stream.erl @@ -48,7 +48,7 @@ peercert(_S) -> getstat(Socket, Stats) -> case quicer:getstats(Socket, Stats) of - {error, _} -> []; + {error, _} -> {error, closed}; Res -> {ok, Res} end. diff --git a/rebar.config b/rebar.config index ac0604728..67efca3f6 100644 --- a/rebar.config +++ b/rebar.config @@ -54,7 +54,7 @@ , {snabbkaffe, {git, "https://github.com/kafka4beam/snabbkaffe.git", {tag, "0.13.0"}}} , {hocon, {git, "https://github.com/emqx/hocon.git", {tag, "0.5.1"}}} , {emqx_http_lib, {git, "https://github.com/emqx/emqx_http_lib.git", {tag, "0.2.1"}}} - , {quicer, {git, "https://github.com/qzhuyan/quic.git", {branch, "fix/getopt3-free-bin"}}} + , {quicer, {git, "https://github.com/qzhuyan/quic.git", {branch, "demo/3"}}} ]}. {xref_ignores, From 6dc4088f7e169744056cb63c8683c3f054055100 Mon Sep 17 00:00:00 2001 From: William Yang Date: Wed, 5 May 2021 12:41:12 +0200 Subject: [PATCH 006/174] chore(ci): disable centos7 build --- .github/workflows/build_slim_packages.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_slim_packages.yaml b/.github/workflows/build_slim_packages.yaml index 6c9bbf04a..53bbba702 100644 --- a/.github/workflows/build_slim_packages.yaml +++ b/.github/workflows/build_slim_packages.yaml @@ -18,7 +18,7 @@ jobs: - erl23.2.7.2-emqx-2 os: - ubuntu20.04 - - centos7 + #- centos7 container: emqx/build-env:${{ matrix.erl_otp }}-${{ matrix.os }} From 06f9674ce3ce57d1fc0496729eacee744a8b46b7 Mon Sep 17 00:00:00 2001 From: William Yang Date: Wed, 5 May 2021 13:30:50 +0200 Subject: [PATCH 007/174] feat(quic): add quicer to application deps list. --- apps/emqx/src/emqx.app.src | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/emqx/src/emqx.app.src b/apps/emqx/src/emqx.app.src index e909702ae..a6984370e 100644 --- a/apps/emqx/src/emqx.app.src +++ b/apps/emqx/src/emqx.app.src @@ -4,7 +4,7 @@ {vsn, "5.0.0"}, % strict semver, bump manually! {modules, []}, {registered, []}, - {applications, [kernel,stdlib,gproc,gen_rpc,esockd,cowboy,sasl,os_mon]}, + {applications, [kernel,stdlib,gproc,gen_rpc,esockd,cowboy,sasl,os_mon,quicer]}, {mod, {emqx_app,[]}}, {env, []}, {licenses, ["Apache-2.0"]}, From 14057c033d5d5d1bc6746b5e60f70cda17fe1169 Mon Sep 17 00:00:00 2001 From: William Yang Date: Fri, 7 May 2021 13:43:48 +0200 Subject: [PATCH 008/174] feat(quic): support stop/start quic listeners. --- apps/emqx/src/emqx.erl | 4 ++-- apps/emqx/src/emqx_app.erl | 3 +++ apps/emqx/src/emqx_listeners.erl | 4 +++- rebar.config | 2 +- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/apps/emqx/src/emqx.erl b/apps/emqx/src/emqx.erl index 2d2e4eb52..449116738 100644 --- a/apps/emqx/src/emqx.erl +++ b/apps/emqx/src/emqx.erl @@ -239,10 +239,10 @@ reboot() -> -ifdef(EMQX_ENTERPRISE). default_started_applications() -> - [gproc, esockd, ranch, cowboy, ekka, emqx]. + [gproc, esockd, ranch, cowboy, ekka, quicer, emqx]. -else. default_started_applications() -> - [gproc, esockd, ranch, cowboy, ekka, emqx, emqx_modules]. + [gproc, esockd, ranch, cowboy, ekka, quicer, emqx, emqx_modules]. -endif. %%-------------------------------------------------------------------- diff --git a/apps/emqx/src/emqx_app.erl b/apps/emqx/src/emqx_app.erl index 26b81c8e7..d786a42b9 100644 --- a/apps/emqx/src/emqx_app.erl +++ b/apps/emqx/src/emqx_app.erl @@ -49,6 +49,9 @@ start(_Type, _Args) -> ok = emqx_plugins:init(), _ = emqx_plugins:load(), _ = start_ce_modules(), + %% @fixme unsure why we need this. + quicer_nif:open_lib(), + quicer_nif:reg_open(), emqx_boot:is_enabled(listeners) andalso (ok = emqx_listeners:start()), register(emqx, self()), ok = emqx_alarm_handler:load(), diff --git a/apps/emqx/src/emqx_listeners.erl b/apps/emqx/src/emqx_listeners.erl index b3d6bf319..2d7a77e65 100644 --- a/apps/emqx/src/emqx_listeners.erl +++ b/apps/emqx/src/emqx_listeners.erl @@ -141,7 +141,7 @@ start_listener(Proto, ListenOn, Options) when Proto == https; Proto == wss -> start_http_listener(fun cowboy:start_tls/3, 'mqtt:wss', ListenOn, ranch_opts(Options), ws_opts(Options)); -%% MQTT over QUIC +%% Start MQTT/QUIC listener start_listener(quic, ListenOn, Options) -> SSLOpts = proplists:get_value(ssl_options, Options), ListenOpts = [ {cert, proplists:get_value(certfile, SSLOpts)} @@ -253,6 +253,8 @@ stop_listener(Proto, ListenOn, _Opts) when Proto == http; Proto == ws -> cowboy:stop_listener(ws_name('mqtt:ws', ListenOn)); stop_listener(Proto, ListenOn, _Opts) when Proto == https; Proto == wss -> cowboy:stop_listener(ws_name('mqtt:wss', ListenOn)); +stop_listener(quic, _ListenOn, _Opts) -> + quicer:stop_listener('mqtt:quic'); stop_listener(Proto, ListenOn, _Opts) -> esockd:close(Proto, ListenOn). diff --git a/rebar.config b/rebar.config index 67efca3f6..b602005f6 100644 --- a/rebar.config +++ b/rebar.config @@ -54,7 +54,7 @@ , {snabbkaffe, {git, "https://github.com/kafka4beam/snabbkaffe.git", {tag, "0.13.0"}}} , {hocon, {git, "https://github.com/emqx/hocon.git", {tag, "0.5.1"}}} , {emqx_http_lib, {git, "https://github.com/emqx/emqx_http_lib.git", {tag, "0.2.1"}}} - , {quicer, {git, "https://github.com/qzhuyan/quic.git", {branch, "demo/3"}}} + , {quicer, {git, "https://github.com/qzhuyan/quic.git", {branch, "dev/william/main-prepare-emqx"}}} ]}. {xref_ignores, From e062be2b0e1e4835358b6aa21d14b9d77cf372bf Mon Sep 17 00:00:00 2001 From: William Yang Date: Sat, 8 May 2021 23:31:29 +0200 Subject: [PATCH 009/174] feat(quic): reload quicer lib before start listener --- apps/emqx/src/emqx_app.erl | 3 --- apps/emqx/src/emqx_listeners.erl | 3 +++ apps/emqx/test/emqx_listeners_SUITE.erl | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/emqx/src/emqx_app.erl b/apps/emqx/src/emqx_app.erl index d786a42b9..26b81c8e7 100644 --- a/apps/emqx/src/emqx_app.erl +++ b/apps/emqx/src/emqx_app.erl @@ -49,9 +49,6 @@ start(_Type, _Args) -> ok = emqx_plugins:init(), _ = emqx_plugins:load(), _ = start_ce_modules(), - %% @fixme unsure why we need this. - quicer_nif:open_lib(), - quicer_nif:reg_open(), emqx_boot:is_enabled(listeners) andalso (ok = emqx_listeners:start()), register(emqx, self()), ok = emqx_alarm_handler:load(), diff --git a/apps/emqx/src/emqx_listeners.erl b/apps/emqx/src/emqx_listeners.erl index 2d7a77e65..d66606895 100644 --- a/apps/emqx/src/emqx_listeners.erl +++ b/apps/emqx/src/emqx_listeners.erl @@ -143,6 +143,9 @@ start_listener(Proto, ListenOn, Options) when Proto == https; Proto == wss -> %% Start MQTT/QUIC listener start_listener(quic, ListenOn, Options) -> + %% @fixme unsure why we need reopen lib and reopen config. + quicer_nif:open_lib(), + quicer_nif:reg_open(), SSLOpts = proplists:get_value(ssl_options, Options), ListenOpts = [ {cert, proplists:get_value(certfile, SSLOpts)} , {key, proplists:get_value(keyfile, SSLOpts)} diff --git a/apps/emqx/test/emqx_listeners_SUITE.erl b/apps/emqx/test/emqx_listeners_SUITE.erl index 53f388dfa..41b9126b0 100644 --- a/apps/emqx/test/emqx_listeners_SUITE.erl +++ b/apps/emqx/test/emqx_listeners_SUITE.erl @@ -28,6 +28,7 @@ all() -> emqx_ct:all(?MODULE). init_per_suite(Config) -> NewConfig = generate_config(), application:ensure_all_started(esockd), + application:ensure_all_started(quicer), application:ensure_all_started(cowboy), lists:foreach(fun set_app_env/1, NewConfig), Config. From f9a113477e059831655ba9c1a57a683a667a434f Mon Sep 17 00:00:00 2001 From: William Yang Date: Sun, 9 May 2021 00:00:40 +0200 Subject: [PATCH 010/174] feat(quic): use quicer:getstat instead. --- apps/emqx/src/emqx_quic_stream.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/emqx/src/emqx_quic_stream.erl b/apps/emqx/src/emqx_quic_stream.erl index 056c5dd24..22d194632 100644 --- a/apps/emqx/src/emqx_quic_stream.erl +++ b/apps/emqx/src/emqx_quic_stream.erl @@ -47,7 +47,7 @@ peercert(_S) -> nossl. getstat(Socket, Stats) -> - case quicer:getstats(Socket, Stats) of + case quicer:getstat(Socket, Stats) of {error, _} -> {error, closed}; Res -> {ok, Res} end. From 1ffd2cf2459479ed2e8c1465a9184999e4f6c904 Mon Sep 17 00:00:00 2001 From: William Yang Date: Tue, 11 May 2021 08:28:11 +0200 Subject: [PATCH 011/174] chore(config): adapt to new config format --- apps/emqx/etc/emqx.conf | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/emqx/etc/emqx.conf b/apps/emqx/etc/emqx.conf index 19100c41e..bb4c76534 100644 --- a/apps/emqx/etc/emqx.conf +++ b/apps/emqx/etc/emqx.conf @@ -2170,7 +2170,7 @@ listener.quic.external = 14567 ## The path of WebSocket MQTT endpoint ## ## Value: URL Path -listener.quic.external.mqtt_path = /mqtt +listener.quic.external.mqtt_path = "/mqtt" ## The acceptor pool for external MQTT/QUIC listener. ## @@ -2244,14 +2244,14 @@ listener.quic.external.access.1 = allow all ## See: listener.ssl.$name.keyfile ## ## Value: File -listener.quic.external.keyfile = {{ platform_etc_dir }}/certs/key.pem +listener.quic.external.keyfile = "{{ platform_etc_dir }}/certs/key.pem" ## Path to a file containing the user certificate. ## ## See: listener.ssl.$name.certfile ## ## Value: File -listener.quic.external.certfile = {{ platform_etc_dir }}/certs/cert.pem +listener.quic.external.certfile = "{{ platform_etc_dir }}/certs/cert.pem" ## Path to the file containing PEM-encoded CA certificates. ## @@ -2294,7 +2294,7 @@ listener.quic.external.certfile = {{ platform_etc_dir }}/certs/cert.pem ## See: listener.ssl.$name.ciphers ## ## Value: Ciphers -listener.quic.external.ciphers = TLS_AES_256_GCM_SHA384,TLS_AES_128_GCM_SHA256,TLS_CHACHA20_POLY1305_SHA256,TLS_AES_128_CCM_SHA256,TLS_AES_128_CCM_8_SHA256,ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384,ECDHE-ECDSA-AES256-SHA384,ECDHE-RSA-AES256-SHA384,ECDHE-ECDSA-DES-CBC3-SHA,ECDH-ECDSA-AES256-GCM-SHA384,ECDH-RSA-AES256-GCM-SHA384,ECDH-ECDSA-AES256-SHA384,ECDH-RSA-AES256-SHA384,DHE-DSS-AES256-GCM-SHA384,DHE-DSS-AES256-SHA256,AES256-GCM-SHA384,AES256-SHA256,ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-RSA-AES128-GCM-SHA256,ECDHE-ECDSA-AES128-SHA256,ECDHE-RSA-AES128-SHA256,ECDH-ECDSA-AES128-GCM-SHA256,ECDH-RSA-AES128-GCM-SHA256,ECDH-ECDSA-AES128-SHA256,ECDH-RSA-AES128-SHA256,DHE-DSS-AES128-GCM-SHA256,DHE-DSS-AES128-SHA256,AES128-GCM-SHA256,AES128-SHA256,ECDHE-ECDSA-AES256-SHA,ECDHE-RSA-AES256-SHA,DHE-DSS-AES256-SHA,ECDH-ECDSA-AES256-SHA,ECDH-RSA-AES256-SHA,AES256-SHA,ECDHE-ECDSA-AES128-SHA,ECDHE-RSA-AES128-SHA,DHE-DSS-AES128-SHA,ECDH-ECDSA-AES128-SHA,ECDH-RSA-AES128-SHA,AES128-SHA +listener.quic.external.ciphers = "TLS_AES_256_GCM_SHA384,TLS_AES_128_GCM_SHA256,TLS_CHACHA20_POLY1305_SHA256,TLS_AES_128_CCM_SHA256,TLS_AES_128_CCM_8_SHA256,ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384,ECDHE-ECDSA-AES256-SHA384,ECDHE-RSA-AES256-SHA384,ECDHE-ECDSA-DES-CBC3-SHA,ECDH-ECDSA-AES256-GCM-SHA384,ECDH-RSA-AES256-GCM-SHA384,ECDH-ECDSA-AES256-SHA384,ECDH-RSA-AES256-SHA384,DHE-DSS-AES256-GCM-SHA384,DHE-DSS-AES256-SHA256,AES256-GCM-SHA384,AES256-SHA256,ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-RSA-AES128-GCM-SHA256,ECDHE-ECDSA-AES128-SHA256,ECDHE-RSA-AES128-SHA256,ECDH-ECDSA-AES128-GCM-SHA256,ECDH-RSA-AES128-GCM-SHA256,ECDH-ECDSA-AES128-SHA256,ECDH-RSA-AES128-SHA256,DHE-DSS-AES128-GCM-SHA256,DHE-DSS-AES128-SHA256,AES128-GCM-SHA256,AES128-SHA256,ECDHE-ECDSA-AES256-SHA,ECDHE-RSA-AES256-SHA,DHE-DSS-AES256-SHA,ECDH-ECDSA-AES256-SHA,ECDH-RSA-AES256-SHA,AES256-SHA,ECDHE-ECDSA-AES128-SHA,ECDHE-RSA-AES128-SHA,DHE-DSS-AES128-SHA,ECDH-ECDSA-AES128-SHA,ECDH-RSA-AES128-SHA,AES128-SHA" ## Ciphers for TLS PSK. ## Note that 'listener.quic.external.ciphers' and 'listener.quic.external.psk_ciphers' cannot @@ -2459,7 +2459,7 @@ listener.quic.external.allow_origin_absence = true ## Comma separated list of allowed origin in header for secure websocket connection ## ## Value: http://url eg. https://localhost:8084, https://127.0.0.1:8084 -listener.quic.external.check_origins = https://localhost:8084, https://127.0.0.1:8084 +listener.quic.external.check_origins = "https://localhost:8084, https://127.0.0.1:8084" ## CONFIG_SECTION_END=listeners ================================================ From 5356668eaccb33a6e689455cf6dd9ca680df65b2 Mon Sep 17 00:00:00 2001 From: William Yang Date: Thu, 10 Jun 2021 16:18:16 +0200 Subject: [PATCH 012/174] feat(quic): adapt to hocon schema --- apps/emqx/etc/emqx.conf | 31 ++++--------------------------- 1 file changed, 4 insertions(+), 27 deletions(-) diff --git a/apps/emqx/etc/emqx.conf b/apps/emqx/etc/emqx.conf index bb4c76534..fd6de554e 100644 --- a/apps/emqx/etc/emqx.conf +++ b/apps/emqx/etc/emqx.conf @@ -1841,7 +1841,7 @@ listener.ws.external.check_origins = "http://localhost:18083, http://127.0.0.1:1 ##-------------------------------------------------------------------- ## External WebSocket/SSL listener for MQTT Protocol -## listener.wss.$name is the IP address and port that the MQTT/WebSocket/SSL +## listener.wss.$name.endpoint is the IP address and port that the MQTT/WebSocket/SSL ## listener will bind. ## ## Value: IP:Port | Port @@ -2159,18 +2159,13 @@ listener.wss.external.check_origins = "https://localhost:8084, https://127.0.0.1 ##-------------------------------------------------------------------- ## External QUIC listener for MQTT Protocol -## listener.quic.$name is the IP address and port that the MQTT/QUIC +## listener.quic.$name.endpoint is the IP address and port that the MQTT/QUIC ## listener will bind. ## ## Value: IP:Port | Port ## ## Examples: 14567, 127.0.0.1:14567, ::1:14567 -listener.quic.external = 14567 - -## The path of WebSocket MQTT endpoint -## -## Value: URL Path -listener.quic.external.mqtt_path = "/mqtt" +listener.quic.external.endpoint = 14567 ## The acceptor pool for external MQTT/QUIC listener. ## @@ -2204,25 +2199,7 @@ listener.quic.external.zone = external ## See: listener.tcp.$name.access. ## ## Value: ACL Rule -listener.quic.external.access.1 = allow all - -## If set to true, the server fails if the client does not have a Sec-WebSocket-Protocol to send. -## Set to false for WeChat MiniApp. -## -## Value: true | false -## listener.quic.external.fail_if_no_subprotocol = true - -## Supported subprotocols -## -## Default: mqtt, mqtt-v3, mqtt-v3.1.1, mqtt-v5 -## listener.quic.external.supported_subprotocols = mqtt, mqtt-v3, mqtt-v3.1.1, mqtt-v5 - -## Enable the Proxy Protocol V1/2 support. -## -## See: listener.tcp.$name.proxy_protocol -## -## Value: on | off -## listener.quic.external.proxy_protocol = on +listener.quic.external.access.1 = "allow all" ## Sets the timeout for proxy protocol. ## From 14614fbe33ee27a26e705945261994f8534374b9 Mon Sep 17 00:00:00 2001 From: William Yang Date: Tue, 11 May 2021 22:52:25 +0200 Subject: [PATCH 013/174] feat(quic): adapt to new quicer API. --- apps/emqx/src/emqx_listeners.erl | 2 ++ apps/emqx/src/emqx_quic_stream.erl | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/emqx/src/emqx_listeners.erl b/apps/emqx/src/emqx_listeners.erl index d66606895..6ee07e17b 100644 --- a/apps/emqx/src/emqx_listeners.erl +++ b/apps/emqx/src/emqx_listeners.erl @@ -150,6 +150,8 @@ start_listener(quic, ListenOn, Options) -> ListenOpts = [ {cert, proplists:get_value(certfile, SSLOpts)} , {key, proplists:get_value(keyfile, SSLOpts)} , {alpn, ["mqtt"]} + , {peer_unidi_stream_count, 1} + , {peer_bidi_stream_count, 10} , {conn_acceptors, 32} ], ConnectionOpts = [ {conn_callback, emqx_quic_connection} diff --git a/apps/emqx/src/emqx_quic_stream.erl b/apps/emqx/src/emqx_quic_stream.erl index 22d194632..4fbe2ed65 100644 --- a/apps/emqx/src/emqx_quic_stream.erl +++ b/apps/emqx/src/emqx_quic_stream.erl @@ -49,7 +49,7 @@ peercert(_S) -> getstat(Socket, Stats) -> case quicer:getstat(Socket, Stats) of {error, _} -> {error, closed}; - Res -> {ok, Res} + Res -> Res end. setopts(_Socket, _Opts) -> From e63f86e5f0882266a0d35ff1e0737609195c157f Mon Sep 17 00:00:00 2001 From: William Yang Date: Wed, 12 May 2021 12:17:04 +0200 Subject: [PATCH 014/174] Revert "chore(ci): disable centos7 build" This reverts commit 22e8da1b37492af7def46329ba97dd4cc7741bdf. --- .github/workflows/build_slim_packages.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_slim_packages.yaml b/.github/workflows/build_slim_packages.yaml index 53bbba702..6c9bbf04a 100644 --- a/.github/workflows/build_slim_packages.yaml +++ b/.github/workflows/build_slim_packages.yaml @@ -18,7 +18,7 @@ jobs: - erl23.2.7.2-emqx-2 os: - ubuntu20.04 - #- centos7 + - centos7 container: emqx/build-env:${{ matrix.erl_otp }}-${{ matrix.os }} From 3200bbb301d816ed7112c676e99b56f3cea0d25b Mon Sep 17 00:00:00 2001 From: William Yang Date: Tue, 1 Jun 2021 09:53:48 +0200 Subject: [PATCH 015/174] fix(build): test with centos branch --- rebar.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rebar.config b/rebar.config index b602005f6..394296e23 100644 --- a/rebar.config +++ b/rebar.config @@ -54,7 +54,7 @@ , {snabbkaffe, {git, "https://github.com/kafka4beam/snabbkaffe.git", {tag, "0.13.0"}}} , {hocon, {git, "https://github.com/emqx/hocon.git", {tag, "0.5.1"}}} , {emqx_http_lib, {git, "https://github.com/emqx/emqx_http_lib.git", {tag, "0.2.1"}}} - , {quicer, {git, "https://github.com/qzhuyan/quic.git", {branch, "dev/william/main-prepare-emqx"}}} + , {quicer, {git, "https://github.com/qzhuyan/quic.git", {branch, "dev/william/main-prepare-emqx-centos7"}}} ]}. {xref_ignores, From bb6459ba3ada82dc1f74be8c1db78ee6a9928a01 Mon Sep 17 00:00:00 2001 From: William Yang Date: Fri, 11 Jun 2021 08:20:23 +0200 Subject: [PATCH 016/174] build: add quic dep in app/emqx/rebar.config --- apps/emqx/rebar.config | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/emqx/rebar.config b/apps/emqx/rebar.config index 7e649ae80..d30946a02 100644 --- a/apps/emqx/rebar.config +++ b/apps/emqx/rebar.config @@ -20,6 +20,7 @@ , {pbkdf2, {git, "https://github.com/emqx/erlang-pbkdf2.git", {branch, "2.0.4"}}} , {recon, {git, "https://github.com/ferd/recon", {tag, "2.5.1"}}} , {snabbkaffe, {git, "https://github.com/kafka4beam/snabbkaffe.git", {tag, "0.13.0"}}} + , {quicer, {git, "https://github.com/qzhuyan/quic.git", {branch, "dev/william/main-prepare-emqx-centos7"}}} ]}. {plugins, [rebar3_proper]}. From 68844cefd9a119420a0dc5f73fc8d76109ebc082 Mon Sep 17 00:00:00 2001 From: William Yang Date: Fri, 11 Jun 2021 11:12:07 +0200 Subject: [PATCH 017/174] feat(quic): update emqx_schema for quic --- apps/emqx/etc/emqx.conf | 220 +++++++++++++++++----------------- apps/emqx/src/emqx_schema.erl | 34 +++++- 2 files changed, 143 insertions(+), 111 deletions(-) diff --git a/apps/emqx/etc/emqx.conf b/apps/emqx/etc/emqx.conf index fd6de554e..b115964cf 100644 --- a/apps/emqx/etc/emqx.conf +++ b/apps/emqx/etc/emqx.conf @@ -2184,43 +2184,43 @@ listener.quic.external.max_connections = 16 ## Value: Number listener.quic.external.max_conn_rate = 1000 -## Simulate the {active, N} option for the MQTT/QUIC connections. -## -## Value: Number -listener.quic.external.active_n = 100 +# ## Simulate the {active, N} option for the MQTT/QUIC connections. +# ## +# ## Value: Number +# listener.quic.external.active_n = 100 ## Zone of the external MQTT/QUIC listener belonged to. ## ## Value: String listener.quic.external.zone = external -## The access control rules for the MQTT/QUIC listener. -## -## See: listener.tcp.$name.access. -## -## Value: ACL Rule -listener.quic.external.access.1 = "allow all" +# ## The access control rules for the MQTT/QUIC listener. +# ## +# ## See: listener.tcp.$name.access. +# ## +# ## Value: ACL Rule +# listener.quic.external.access.1 = "allow all" -## Sets the timeout for proxy protocol. -## -## See: listener.tcp.$name.proxy_protocol_timeout -## -## Value: Duration -## listener.quic.external.proxy_protocol_timeout = 3s +# ## Sets the timeout for proxy protocol. +# ## +# ## See: listener.tcp.$name.proxy_protocol_timeout +# ## +# ## Value: Duration +# ## listener.quic.external.proxy_protocol_timeout = 3s -## TLS versions only to protect from POODLE attack. -## -## See: listener.ssl.$name.tls_versions -## -## Value: String, seperated by ',' -## NOTE: Do not use tlsv1.3 if emqx is running on OTP-22 or earlier -## listener.quic.external.tls_versions = tlsv1.3,tlsv1.2,tlsv1.1,tlsv1 +# ## TLS versions only to protect from POODLE attack. +# ## +# ## See: listener.ssl.$name.tls_versions +# ## +# ## Value: String, seperated by ',' +# ## NOTE: Do not use tlsv1.3 if emqx is running on OTP-22 or earlier +# ## listener.quic.external.tls_versions = tlsv1.3,tlsv1.2,tlsv1.1,tlsv1 -## Path to the file containing the user's private PEM-encoded key. -## -## See: listener.ssl.$name.keyfile -## -## Value: File +# ## Path to the file containing the user's private PEM-encoded key. +# ## +# ## See: listener.ssl.$name.keyfile +# ## +# ## Value: File listener.quic.external.keyfile = "{{ platform_etc_dir }}/certs/key.pem" ## Path to a file containing the user certificate. @@ -2230,100 +2230,100 @@ listener.quic.external.keyfile = "{{ platform_etc_dir }}/certs/key.pem" ## Value: File listener.quic.external.certfile = "{{ platform_etc_dir }}/certs/cert.pem" -## Path to the file containing PEM-encoded CA certificates. -## -## See: listener.ssl.$name.cacert -## -## Value: File -## listener.quic.external.cacertfile = {{ platform_etc_dir }}/certs/cacert.pem +# ## Path to the file containing PEM-encoded CA certificates. +# ## +# ## See: listener.ssl.$name.cacert +# ## +# ## Value: File +# ## listener.quic.external.cacertfile = {{ platform_etc_dir }}/certs/cacert.pem -## Maximum number of non-self-issued intermediate certificates that -## can follow the peer certificate in a valid certification path. -## -## See: listener.ssl.external.depth -## -## Value: Number -## listener.quic.external.depth = 10 +# ## Maximum number of non-self-issued intermediate certificates that +# ## can follow the peer certificate in a valid certification path. +# ## +# ## See: listener.ssl.external.depth +# ## +# ## Value: Number +# ## listener.quic.external.depth = 10 -## String containing the user's password. Only used if the private keyfile -## is password-protected. -## -## See: listener.ssl.$name.key_password -## -## Value: String -## listener.quic.external.key_password = yourpass +# ## String containing the user's password. Only used if the private keyfile +# ## is password-protected. +# ## +# ## See: listener.ssl.$name.key_password +# ## +# ## Value: String +# ## listener.quic.external.key_password = yourpass -## See: listener.ssl.$name.dhfile -## -## Value: File -## listener.ssl.external.dhfile = {{ platform_etc_dir }}/certs/dh-params.pem +# ## See: listener.ssl.$name.dhfile +# ## +# ## Value: File +# ## listener.ssl.external.dhfile = {{ platform_etc_dir }}/certs/dh-params.pem -## See: listener.ssl.$name.verify -## -## Value: verify_peer | verify_none -## listener.quic.external.verify = verify_peer +# ## See: listener.ssl.$name.verify +# ## +# ## Value: verify_peer | verify_none +# ## listener.quic.external.verify = verify_peer -## See: listener.ssl.$name.fail_if_no_peer_cert -## -## Value: false | true -## listener.quic.external.fail_if_no_peer_cert = true +# ## See: listener.ssl.$name.fail_if_no_peer_cert +# ## +# ## Value: false | true +# ## listener.quic.external.fail_if_no_peer_cert = true -## See: listener.ssl.$name.ciphers -## -## Value: Ciphers +# ## See: listener.ssl.$name.ciphers +# ## +# ## Value: Ciphers listener.quic.external.ciphers = "TLS_AES_256_GCM_SHA384,TLS_AES_128_GCM_SHA256,TLS_CHACHA20_POLY1305_SHA256,TLS_AES_128_CCM_SHA256,TLS_AES_128_CCM_8_SHA256,ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384,ECDHE-ECDSA-AES256-SHA384,ECDHE-RSA-AES256-SHA384,ECDHE-ECDSA-DES-CBC3-SHA,ECDH-ECDSA-AES256-GCM-SHA384,ECDH-RSA-AES256-GCM-SHA384,ECDH-ECDSA-AES256-SHA384,ECDH-RSA-AES256-SHA384,DHE-DSS-AES256-GCM-SHA384,DHE-DSS-AES256-SHA256,AES256-GCM-SHA384,AES256-SHA256,ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-RSA-AES128-GCM-SHA256,ECDHE-ECDSA-AES128-SHA256,ECDHE-RSA-AES128-SHA256,ECDH-ECDSA-AES128-GCM-SHA256,ECDH-RSA-AES128-GCM-SHA256,ECDH-ECDSA-AES128-SHA256,ECDH-RSA-AES128-SHA256,DHE-DSS-AES128-GCM-SHA256,DHE-DSS-AES128-SHA256,AES128-GCM-SHA256,AES128-SHA256,ECDHE-ECDSA-AES256-SHA,ECDHE-RSA-AES256-SHA,DHE-DSS-AES256-SHA,ECDH-ECDSA-AES256-SHA,ECDH-RSA-AES256-SHA,AES256-SHA,ECDHE-ECDSA-AES128-SHA,ECDHE-RSA-AES128-SHA,DHE-DSS-AES128-SHA,ECDH-ECDSA-AES128-SHA,ECDH-RSA-AES128-SHA,AES128-SHA" -## Ciphers for TLS PSK. -## Note that 'listener.quic.external.ciphers' and 'listener.quic.external.psk_ciphers' cannot -## be configured at the same time. -## See 'https://tools.ietf.org/html/rfc4279#section-2'. -## listener.quic.external.psk_ciphers = PSK-AES128-CBC-SHA,PSK-AES256-CBC-SHA,PSK-3DES-EDE-CBC-SHA,PSK-RC4-SHA +# ## Ciphers for TLS PSK. +# ## Note that 'listener.quic.external.ciphers' and 'listener.quic.external.psk_ciphers' cannot +# ## be configured at the same time. +# ## See 'https://tools.ietf.org/html/rfc4279#section-2'. +# ## listener.quic.external.psk_ciphers = PSK-AES128-CBC-SHA,PSK-AES256-CBC-SHA,PSK-3DES-EDE-CBC-SHA,PSK-RC4-SHA -## See: listener.ssl.$name.secure_renegotiate -## -## Value: on | off -## listener.quic.external.secure_renegotiate = off +# ## See: listener.ssl.$name.secure_renegotiate +# ## +# ## Value: on | off +# ## listener.quic.external.secure_renegotiate = off -## See: listener.ssl.$name.reuse_sessions -## -## Value: on | off -## listener.quic.external.reuse_sessions = on +# ## See: listener.ssl.$name.reuse_sessions +# ## +# ## Value: on | off +# ## listener.quic.external.reuse_sessions = on -## See: listener.ssl.$name.honor_cipher_order -## -## Value: on | off -## listener.quic.external.honor_cipher_order = on +# ## See: listener.ssl.$name.honor_cipher_order +# ## +# ## Value: on | off +# ## listener.quic.external.honor_cipher_order = on -## See: listener.ssl.$name.peer_cert_as_username -## -## Value: cn | dn | crt | pem | md5 -## listener.quic.external.peer_cert_as_username = cn +# ## See: listener.ssl.$name.peer_cert_as_username +# ## +# ## Value: cn | dn | crt | pem | md5 +# ## listener.quic.external.peer_cert_as_username = cn -## See: listener.ssl.$name.peer_cert_as_clientid -## -## Value: cn | dn | crt | pem | md5 -## listener.quic.external.peer_cert_as_clientid = cn +# ## See: listener.ssl.$name.peer_cert_as_clientid +# ## +# ## Value: cn | dn | crt | pem | md5 +# ## listener.quic.external.peer_cert_as_clientid = cn -## TCP backlog for the QUIC connection. -## -## See: listener.tcp.$name.backlog -## -## Value: Number >= 0 -listener.quic.external.backlog = 1024 +# ## TCP backlog for the QUIC connection. +# ## +# ## See: listener.tcp.$name.backlog +# ## +# ## Value: Number >= 0 +# listener.quic.external.backlog = 1024 -## The TCP send timeout for the QUIC connection. -## -## See: listener.tcp.$name.send_timeout -## -## Value: Duration -listener.quic.external.send_timeout = 15s +# ## The TCP send timeout for the QUIC connection. +# ## +# ## See: listener.tcp.$name.send_timeout +# ## +# ## Value: Duration +# listener.quic.external.send_timeout = 15s -## Close the QUIC connection if send timeout. -## -## See: listener.tcp.$name.send_timeout_close -## -## Value: on | off -listener.quic.external.send_timeout_close = on +# ## Close the QUIC connection if send timeout. +# ## +# ## See: listener.tcp.$name.send_timeout_close +# ## +# ## Value: on | off +# listener.quic.external.send_timeout_close = on ## The TCP receive buffer(os kernel) for the QUIC connections. ## @@ -2424,19 +2424,19 @@ listener.quic.external.send_timeout_close = on ## Whether a WebSocket message is allowed to contain multiple MQTT packets ## ## Value: single | multiple -listener.quic.external.mqtt_piggyback = multiple +#listener.quic.external.mqtt_piggyback = multiple ## Enable origin check in header for secure websocket connection ## ## Value: true | false (default false) -listener.quic.external.check_origin_enable = false +#listener.quic.external.check_origin_enable = false ## Allow origin to be absent in header in secure websocket connection when check_origin_enable is true ## ## Value: true | false (default true) -listener.quic.external.allow_origin_absence = true +#listener.quic.external.allow_origin_absence = true ## Comma separated list of allowed origin in header for secure websocket connection ## ## Value: http://url eg. https://localhost:8084, https://127.0.0.1:8084 -listener.quic.external.check_origins = "https://localhost:8084, https://127.0.0.1:8084" +#listener.quic.external.check_origins = "https://localhost:8084, https://127.0.0.1:8084" ## CONFIG_SECTION_END=listeners ================================================ diff --git a/apps/emqx/src/emqx_schema.erl b/apps/emqx/src/emqx_schema.erl index 316f4b77c..d1c163e58 100644 --- a/apps/emqx/src/emqx_schema.erl +++ b/apps/emqx/src/emqx_schema.erl @@ -282,6 +282,7 @@ fields("listener") -> , {"ssl", ref("ssl_listener")} , {"ws", ref("ws_listener")} , {"wss", ref("wss_listener")} + , {"quic", ref("quic_listener")} ]; fields("tcp_listener") -> @@ -296,6 +297,9 @@ fields("ws_listener") -> fields("wss_listener") -> [ {"$name", ref("wss_listener_settings")}]; +fields("quic_listener") -> + [ {"$name", ref("quic_listener_settings")}]; + fields("listener_settings") -> [ {"endpoint", t(union(ip_port(), integer()))} , {"acceptors", t(integer(), undefined, 8)} @@ -356,6 +360,32 @@ fields("wss_listener_settings") -> Settings = lists:ukeymerge(1, Ssl, fields("ws_listener_settings")), lists:keydelete("high_watermark", 1, Settings); +fields("quic_listener_settings") -> + Unsupported = [ "max_connections" + , "max_conn_rate" + , "active_n" + , "access" + , "proxy_protocol" + , "proxy_protocol_timeout" + , "backlog" + , "send_timeout" + , "send_timeout_close" + , "recvbuf" + , "sndbuf" + , "buffer" + , "high_watermark" + , "tune_buffer" + , "nodelay" + , "reuseaddr" + ], + lists:foldl(fun(K, Acc) -> + lists:keydelete(K, 1, Acc) + end, + [ {"certfile", t(string(), "emqx.certfile", undefined)} + , {"keyfile", t(string(), "emqx.keyfile", undefined)} + | fields("listener_settings")], + Unsupported); + fields("access") -> [ {"$id", t(string(), undefined, undefined)}]; @@ -772,7 +802,9 @@ tr_listeners(Conf) -> lists:flatten([TcpListeners("tcp", Name) || Name <- keys("listener.tcp", Conf)] ++ [TcpListeners("ws", Name) || Name <- keys("listener.ws", Conf)] ++ [SslListeners("ssl", Name) || Name <- keys("listener.ssl", Conf)] - ++ [SslListeners("wss", Name) || Name <- keys("listener.wss", Conf)]). + ++ [SslListeners("wss", Name) || Name <- keys("listener.wss", Conf)] + ++ [SslListeners("quic", Name) || Name <- keys("listener.quic", Conf)] + ). tr_modules(Conf) -> Subscriptions = fun() -> From af2faed10740b14071b78a8ab1fc5a4f52247890 Mon Sep 17 00:00:00 2001 From: William Yang Date: Mon, 14 Jun 2021 11:29:23 +0200 Subject: [PATCH 018/174] feat(quic): switch to deps on emqx quicer repo --- apps/emqx/rebar.config | 2 +- rebar.config | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/emqx/rebar.config b/apps/emqx/rebar.config index d30946a02..5719ec0af 100644 --- a/apps/emqx/rebar.config +++ b/apps/emqx/rebar.config @@ -20,7 +20,7 @@ , {pbkdf2, {git, "https://github.com/emqx/erlang-pbkdf2.git", {branch, "2.0.4"}}} , {recon, {git, "https://github.com/ferd/recon", {tag, "2.5.1"}}} , {snabbkaffe, {git, "https://github.com/kafka4beam/snabbkaffe.git", {tag, "0.13.0"}}} - , {quicer, {git, "https://github.com/qzhuyan/quic.git", {branch, "dev/william/main-prepare-emqx-centos7"}}} + , {quicer, {git, "https://github.com/emqx/quic.git", {tag, "0.0.2"}}} ]}. {plugins, [rebar3_proper]}. diff --git a/rebar.config b/rebar.config index 394296e23..413158842 100644 --- a/rebar.config +++ b/rebar.config @@ -54,7 +54,7 @@ , {snabbkaffe, {git, "https://github.com/kafka4beam/snabbkaffe.git", {tag, "0.13.0"}}} , {hocon, {git, "https://github.com/emqx/hocon.git", {tag, "0.5.1"}}} , {emqx_http_lib, {git, "https://github.com/emqx/emqx_http_lib.git", {tag, "0.2.1"}}} - , {quicer, {git, "https://github.com/qzhuyan/quic.git", {branch, "dev/william/main-prepare-emqx-centos7"}}} + , {quicer, {git, "https://github.com/emqx/quic.git", {tag, "0.0.2"}}} ]}. {xref_ignores, From e34470f9f2e045f76ee4012788c16a6b4b130870 Mon Sep 17 00:00:00 2001 From: William Yang Date: Tue, 15 Jun 2021 09:23:11 +0200 Subject: [PATCH 019/174] feat(quic): remove unsupported configs. --- apps/emqx/etc/emqx.conf | 262 +++++++++------------------------------- 1 file changed, 60 insertions(+), 202 deletions(-) diff --git a/apps/emqx/etc/emqx.conf b/apps/emqx/etc/emqx.conf index b115964cf..f9c1d26ff 100644 --- a/apps/emqx/etc/emqx.conf +++ b/apps/emqx/etc/emqx.conf @@ -2184,43 +2184,21 @@ listener.quic.external.max_connections = 16 ## Value: Number listener.quic.external.max_conn_rate = 1000 -# ## Simulate the {active, N} option for the MQTT/QUIC connections. -# ## -# ## Value: Number -# listener.quic.external.active_n = 100 +## Simulate the {active, N} option for the MQTT/QUIC connections. +## @todo +## Value: Number +## listener.quic.external.active_n = 100 ## Zone of the external MQTT/QUIC listener belonged to. ## ## Value: String listener.quic.external.zone = external -# ## The access control rules for the MQTT/QUIC listener. -# ## -# ## See: listener.tcp.$name.access. -# ## -# ## Value: ACL Rule -# listener.quic.external.access.1 = "allow all" - -# ## Sets the timeout for proxy protocol. -# ## -# ## See: listener.tcp.$name.proxy_protocol_timeout -# ## -# ## Value: Duration -# ## listener.quic.external.proxy_protocol_timeout = 3s - -# ## TLS versions only to protect from POODLE attack. -# ## -# ## See: listener.ssl.$name.tls_versions -# ## -# ## Value: String, seperated by ',' -# ## NOTE: Do not use tlsv1.3 if emqx is running on OTP-22 or earlier -# ## listener.quic.external.tls_versions = tlsv1.3,tlsv1.2,tlsv1.1,tlsv1 - -# ## Path to the file containing the user's private PEM-encoded key. -# ## -# ## See: listener.ssl.$name.keyfile -# ## -# ## Value: File +## Path to the file containing the user's private PEM-encoded key. +## +## See: listener.ssl.$name.keyfile +## +## Value: File listener.quic.external.keyfile = "{{ platform_etc_dir }}/certs/key.pem" ## Path to a file containing the user certificate. @@ -2230,214 +2208,94 @@ listener.quic.external.keyfile = "{{ platform_etc_dir }}/certs/key.pem" ## Value: File listener.quic.external.certfile = "{{ platform_etc_dir }}/certs/cert.pem" -# ## Path to the file containing PEM-encoded CA certificates. -# ## -# ## See: listener.ssl.$name.cacert -# ## -# ## Value: File -# ## listener.quic.external.cacertfile = {{ platform_etc_dir }}/certs/cacert.pem +## Path to the file containing PEM-encoded CA certificates. +## @todo +## See: listener.ssl.$name.cacert +## +## Value: File +## listener.quic.external.cacertfile = {{ platform_etc_dir }}/certs/cacert.pem -# ## Maximum number of non-self-issued intermediate certificates that -# ## can follow the peer certificate in a valid certification path. -# ## -# ## See: listener.ssl.external.depth -# ## -# ## Value: Number -# ## listener.quic.external.depth = 10 +## String containing the user's password. Only used if the private keyfile +## is password-protected. +## @todo +## See: listener.ssl.$name.key_password +## +## Value: String +## listener.quic.external.key_password = yourpass -# ## String containing the user's password. Only used if the private keyfile -# ## is password-protected. -# ## -# ## See: listener.ssl.$name.key_password -# ## -# ## Value: String -# ## listener.quic.external.key_password = yourpass +## See: listener.ssl.$name.verify +## @todo +## Value: verify_peer | verify_none +## listener.quic.external.verify = verify_peer -# ## See: listener.ssl.$name.dhfile -# ## -# ## Value: File -# ## listener.ssl.external.dhfile = {{ platform_etc_dir }}/certs/dh-params.pem +## See: listener.ssl.$name.fail_if_no_peer_cert +## @todo +## Value: false | true +## listener.quic.external.fail_if_no_peer_cert = true -# ## See: listener.ssl.$name.verify -# ## -# ## Value: verify_peer | verify_none -# ## listener.quic.external.verify = verify_peer +## See: listener.ssl.$name.ciphers +## @todo +## Value: Ciphers +listener.quic.external.ciphers = "TLS_AES_256_GCM_SHA384,TLS_AES_128_GCM_SHA256,TLS_CHACHA20_POLY1305_SHA256" -# ## See: listener.ssl.$name.fail_if_no_peer_cert -# ## -# ## Value: false | true -# ## listener.quic.external.fail_if_no_peer_cert = true +## Ciphers for TLS PSK. +## @todo +## Note that 'listener.quic.external.ciphers' and 'listener.quic.external.psk_ciphers' cannot +## be configured at the same time. +## See 'https://tools.ietf.org/html/rfc4279#section-2'. +## listener.quic.external.psk_ciphers = PSK-AES128-CBC-SHA,PSK-AES256-CBC-SHA,PSK-3DES-EDE-CBC-SHA,PSK-RC4-SHA -# ## See: listener.ssl.$name.ciphers -# ## -# ## Value: Ciphers -listener.quic.external.ciphers = "TLS_AES_256_GCM_SHA384,TLS_AES_128_GCM_SHA256,TLS_CHACHA20_POLY1305_SHA256,TLS_AES_128_CCM_SHA256,TLS_AES_128_CCM_8_SHA256,ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384,ECDHE-ECDSA-AES256-SHA384,ECDHE-RSA-AES256-SHA384,ECDHE-ECDSA-DES-CBC3-SHA,ECDH-ECDSA-AES256-GCM-SHA384,ECDH-RSA-AES256-GCM-SHA384,ECDH-ECDSA-AES256-SHA384,ECDH-RSA-AES256-SHA384,DHE-DSS-AES256-GCM-SHA384,DHE-DSS-AES256-SHA256,AES256-GCM-SHA384,AES256-SHA256,ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-RSA-AES128-GCM-SHA256,ECDHE-ECDSA-AES128-SHA256,ECDHE-RSA-AES128-SHA256,ECDH-ECDSA-AES128-GCM-SHA256,ECDH-RSA-AES128-GCM-SHA256,ECDH-ECDSA-AES128-SHA256,ECDH-RSA-AES128-SHA256,DHE-DSS-AES128-GCM-SHA256,DHE-DSS-AES128-SHA256,AES128-GCM-SHA256,AES128-SHA256,ECDHE-ECDSA-AES256-SHA,ECDHE-RSA-AES256-SHA,DHE-DSS-AES256-SHA,ECDH-ECDSA-AES256-SHA,ECDH-RSA-AES256-SHA,AES256-SHA,ECDHE-ECDSA-AES128-SHA,ECDHE-RSA-AES128-SHA,DHE-DSS-AES128-SHA,ECDH-ECDSA-AES128-SHA,ECDH-RSA-AES128-SHA,AES128-SHA" +## See: listener.ssl.$name.honor_cipher_order +## @todo +## Value: on | off +## listener.quic.external.honor_cipher_order = on -# ## Ciphers for TLS PSK. -# ## Note that 'listener.quic.external.ciphers' and 'listener.quic.external.psk_ciphers' cannot -# ## be configured at the same time. -# ## See 'https://tools.ietf.org/html/rfc4279#section-2'. -# ## listener.quic.external.psk_ciphers = PSK-AES128-CBC-SHA,PSK-AES256-CBC-SHA,PSK-3DES-EDE-CBC-SHA,PSK-RC4-SHA - -# ## See: listener.ssl.$name.secure_renegotiate -# ## -# ## Value: on | off -# ## listener.quic.external.secure_renegotiate = off - -# ## See: listener.ssl.$name.reuse_sessions -# ## -# ## Value: on | off -# ## listener.quic.external.reuse_sessions = on - -# ## See: listener.ssl.$name.honor_cipher_order -# ## -# ## Value: on | off -# ## listener.quic.external.honor_cipher_order = on - -# ## See: listener.ssl.$name.peer_cert_as_username -# ## -# ## Value: cn | dn | crt | pem | md5 -# ## listener.quic.external.peer_cert_as_username = cn - -# ## See: listener.ssl.$name.peer_cert_as_clientid -# ## -# ## Value: cn | dn | crt | pem | md5 -# ## listener.quic.external.peer_cert_as_clientid = cn - -# ## TCP backlog for the QUIC connection. -# ## -# ## See: listener.tcp.$name.backlog -# ## -# ## Value: Number >= 0 -# listener.quic.external.backlog = 1024 - -# ## The TCP send timeout for the QUIC connection. -# ## -# ## See: listener.tcp.$name.send_timeout -# ## -# ## Value: Duration +## The send timeout for the QUIC stream. +## @todo +## +## Value: Duration # listener.quic.external.send_timeout = 15s -# ## Close the QUIC connection if send timeout. -# ## -# ## See: listener.tcp.$name.send_timeout_close -# ## -# ## Value: on | off -# listener.quic.external.send_timeout_close = on - -## The TCP receive buffer(os kernel) for the QUIC connections. +## Close the QUIC connection if send timeout. +## @todo +## See: listener.tcp.$name.send_timeout_close ## +## Value: on | off +## listener.quic.external.send_timeout_close = on + +## The receive buffer for the QUIC connections. +## @todo ## See: listener.tcp.$name.recbuf ## ## Value: Bytes ## listener.quic.external.recbuf = 4KB ## The TCP send buffer(os kernel) for the QUIC connections. -## +## @todo ## See: listener.tcp.$name.sndbuf ## ## Value: Bytes ## listener.quic.external.sndbuf = 4KB ## The size of the user-level software buffer used by the driver. -## +## @todo ## See: listener.tcp.$name.buffer ## ## Value: Bytes ## listener.quic.external.buffer = 4KB -## The TCP_NODELAY flag for QUIC connections. -## -## See: listener.tcp.$name.nodelay -## -## Value: true | false -## listener.quic.external.nodelay = true - -## The compress flag for external QUIC connections. -## -## If this Value is set true,the websocket message would be compressed -## -## Value: true | false -## listener.quic.external.compress = true - -## The level of deflate options for external QUIC connections. -## -## See: listener.quic.$name.deflate_opts.level -## -## Value: none | default | best_compression | best_speed -## listener.quic.external.deflate_opts.level = default - -## The mem_level of deflate options for external QUIC connections. -## -## See: listener.quic.$name.deflate_opts.mem_level -## -## Valid range is 1-9 -## listener.quic.external.deflate_opts.mem_level = 8 - -## The strategy of deflate options for external QUIC connections. -## -## See: listener.quic.$name.deflate_opts.strategy -## -## Value: default | filtered | huffman_only | rle -## listener.quic.external.deflate_opts.strategy = default - -## The deflate option for external QUIC connections. -## -## See: listener.quic.$name.deflate_opts.server_context_takeover -## -## Value: takeover | no_takeover -## listener.quic.external.deflate_opts.server_context_takeover = takeover - -## The deflate option for external QUIC connections. -## -## See: listener.quic.$name.deflate_opts.client_context_takeover -## -## Value: takeover | no_takeover -## listener.quic.external.deflate_opts.client_context_takeover = takeover - -## The deflate options for external QUIC connections. -## -## See: listener.quic.$name.deflate_opts.server_max_window_bits -## -## Valid range is 8-15 -## listener.quic.external.deflate_opts.server_max_window_bits = 15 - -## The deflate options for external QUIC connections. -## -## See: listener.quic.$name.deflate_opts.client_max_window_bits -## -## Valid range is 8-15 -## listener.quic.external.deflate_opts.client_max_window_bits = 15 - ## The idle timeout for external QUIC connections. -## +## @todo ## See: listener.quic.$name.idle_timeout ## ## Value: Duration ## listener.quic.external.idle_timeout = 60s ## The max frame size for external QUIC connections. -## +## @todo ## Value: Number ## listener.quic.external.max_frame_size = 0 -## Whether a WebSocket message is allowed to contain multiple MQTT packets -## -## Value: single | multiple -#listener.quic.external.mqtt_piggyback = multiple -## Enable origin check in header for secure websocket connection -## -## Value: true | false (default false) -#listener.quic.external.check_origin_enable = false -## Allow origin to be absent in header in secure websocket connection when check_origin_enable is true -## -## Value: true | false (default true) -#listener.quic.external.allow_origin_absence = true -## Comma separated list of allowed origin in header for secure websocket connection -## -## Value: http://url eg. https://localhost:8084, https://127.0.0.1:8084 -#listener.quic.external.check_origins = "https://localhost:8084, https://127.0.0.1:8084" - ## CONFIG_SECTION_END=listeners ================================================ ## CONFIG_SECTION_BGN=modules ================================================== From fd785240f51a7b32ac46556aec261a15328a6838 Mon Sep 17 00:00:00 2001 From: William Yang Date: Tue, 15 Jun 2021 14:07:57 +0200 Subject: [PATCH 020/174] feat(quic): bump quicer to 0.0.3 --- apps/emqx/rebar.config | 2 +- rebar.config | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/emqx/rebar.config b/apps/emqx/rebar.config index 5719ec0af..1a58efd5b 100644 --- a/apps/emqx/rebar.config +++ b/apps/emqx/rebar.config @@ -20,7 +20,7 @@ , {pbkdf2, {git, "https://github.com/emqx/erlang-pbkdf2.git", {branch, "2.0.4"}}} , {recon, {git, "https://github.com/ferd/recon", {tag, "2.5.1"}}} , {snabbkaffe, {git, "https://github.com/kafka4beam/snabbkaffe.git", {tag, "0.13.0"}}} - , {quicer, {git, "https://github.com/emqx/quic.git", {tag, "0.0.2"}}} + , {quicer, {git, "https://github.com/emqx/quic.git", {tag, "0.0.3"}}} ]}. {plugins, [rebar3_proper]}. diff --git a/rebar.config b/rebar.config index 413158842..d8cee22b5 100644 --- a/rebar.config +++ b/rebar.config @@ -54,7 +54,7 @@ , {snabbkaffe, {git, "https://github.com/kafka4beam/snabbkaffe.git", {tag, "0.13.0"}}} , {hocon, {git, "https://github.com/emqx/hocon.git", {tag, "0.5.1"}}} , {emqx_http_lib, {git, "https://github.com/emqx/emqx_http_lib.git", {tag, "0.2.1"}}} - , {quicer, {git, "https://github.com/emqx/quic.git", {tag, "0.0.2"}}} + , {quicer, {git, "https://github.com/emqx/quic.git", {tag, "0.0.3"}}} ]}. {xref_ignores, From 4e2e2d5635237b8626b124119a0655700edea729 Mon Sep 17 00:00:00 2001 From: William Yang Date: Tue, 15 Jun 2021 15:35:42 +0200 Subject: [PATCH 021/174] feat(quic): update emqx_schema for quic --- apps/emqx/src/emqx_schema.erl | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/apps/emqx/src/emqx_schema.erl b/apps/emqx/src/emqx_schema.erl index d1c163e58..f8a5de183 100644 --- a/apps/emqx/src/emqx_schema.erl +++ b/apps/emqx/src/emqx_schema.erl @@ -361,9 +361,7 @@ fields("wss_listener_settings") -> lists:keydelete("high_watermark", 1, Settings); fields("quic_listener_settings") -> - Unsupported = [ "max_connections" - , "max_conn_rate" - , "active_n" + Unsupported = [ "active_n" , "access" , "proxy_protocol" , "proxy_protocol_timeout" @@ -381,8 +379,9 @@ fields("quic_listener_settings") -> lists:foldl(fun(K, Acc) -> lists:keydelete(K, 1, Acc) end, - [ {"certfile", t(string(), "emqx.certfile", undefined)} - , {"keyfile", t(string(), "emqx.keyfile", undefined)} + [ {"certfile", t(string(), undefined, undefined)} + , {"keyfile", t(string(), undefined, undefined)} + , {"ciphers", t(comma_separated_list(), undefined, "TLS_AES_256_GCM_SHA384,TLS_AES_128_GCM_SHA256,TLS_CHACHA20_POLY1305_SHA256")} | fields("listener_settings")], Unsupported); From b4a9d663aee800141cbfbcaba0fe7d5935b269c3 Mon Sep 17 00:00:00 2001 From: William Yang Date: Wed, 16 Jun 2021 15:08:56 +0200 Subject: [PATCH 022/174] feat(quic): quic conn idle_timeout default 1min --- apps/emqx/src/emqx_listeners.erl | 10 +++++----- apps/emqx/src/emqx_schema.erl | 1 + 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/apps/emqx/src/emqx_listeners.erl b/apps/emqx/src/emqx_listeners.erl index 6ee07e17b..c7d42e2e4 100644 --- a/apps/emqx/src/emqx_listeners.erl +++ b/apps/emqx/src/emqx_listeners.erl @@ -147,17 +147,17 @@ start_listener(quic, ListenOn, Options) -> quicer_nif:open_lib(), quicer_nif:reg_open(), SSLOpts = proplists:get_value(ssl_options, Options), + DefAcceptors = erlang:system_info(schedulers_online) * 8, ListenOpts = [ {cert, proplists:get_value(certfile, SSLOpts)} , {key, proplists:get_value(keyfile, SSLOpts)} , {alpn, ["mqtt"]} - , {peer_unidi_stream_count, 1} - , {peer_bidi_stream_count, 10} - , {conn_acceptors, 32} + , {conn_acceptors, proplists:get_value(acceptors, Options, DefAcceptors)} + , {idle_timeout_ms, proplists:get_value(idle_timeout, Options, 60000)} ], ConnectionOpts = [ {conn_callback, emqx_quic_connection} - , {idle_timeout_ms, 5000} , {peer_unidi_stream_count, 1} - , {peer_bidi_stream_count, 10}], + , {peer_bidi_stream_count, 10} + ], StreamOpts = [], quicer:start_listener('mqtt:quic', ListenOn, {ListenOpts, ConnectionOpts, StreamOpts}). diff --git a/apps/emqx/src/emqx_schema.erl b/apps/emqx/src/emqx_schema.erl index f8a5de183..2660fa668 100644 --- a/apps/emqx/src/emqx_schema.erl +++ b/apps/emqx/src/emqx_schema.erl @@ -382,6 +382,7 @@ fields("quic_listener_settings") -> [ {"certfile", t(string(), undefined, undefined)} , {"keyfile", t(string(), undefined, undefined)} , {"ciphers", t(comma_separated_list(), undefined, "TLS_AES_256_GCM_SHA384,TLS_AES_128_GCM_SHA256,TLS_CHACHA20_POLY1305_SHA256")} + , {"idle_timeout", t(duration(), undefined, 60000)} | fields("listener_settings")], Unsupported); From d1978aaaf278d9524e5456ac36758e23c4c582fb Mon Sep 17 00:00:00 2001 From: William Yang Date: Thu, 17 Jun 2021 09:02:34 +0200 Subject: [PATCH 023/174] chore(quic): fix format --- apps/emqx/src/emqx_quic_stream.erl | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/emqx/src/emqx_quic_stream.erl b/apps/emqx/src/emqx_quic_stream.erl index 4fbe2ed65..e5cd4c3fc 100644 --- a/apps/emqx/src/emqx_quic_stream.erl +++ b/apps/emqx/src/emqx_quic_stream.erl @@ -56,12 +56,12 @@ setopts(_Socket, _Opts) -> ok. getopts(_Socket, _Opts) -> - %% todo - { ok, [{high_watermark, 0}, - {high_msgq_watermark, 0}, - {sndbuf, 0}, - {recbuf, 0}, - {buffer,80000}]}. + %% @todo + {ok, [{high_watermark, 0}, + {high_msgq_watermark, 0}, + {sndbuf, 0}, + {recbuf, 0}, + {buffer,80000}]}. fast_close(Stream) -> quicer:close_stream(Stream), @@ -77,7 +77,7 @@ ensure_ok_or_exit(Fun, Args = [Sock|_]) when is_atom(Fun), is_list(Args) -> {error, Reason} -> fast_close(Sock), exit({shutdown, Reason}); - Result -> Result + Result -> Result end. async_send(Stream, Data, Options) when is_list(Data) -> From 2582fdcfe8059099f48393fd73195e5257f0ebab Mon Sep 17 00:00:00 2001 From: Turtle Date: Fri, 25 Jun 2021 14:49:25 +0800 Subject: [PATCH 024/174] feat(prometheus): Update the configuration file to hocon --- apps/emqx/src/emqx_schema.erl | 19 ++---------- apps/emqx_prometheus/etc/emqx_prometheus.conf | 14 +++------ .../priv/emqx_prometheus.schema | 20 ------------- .../src/emqx_prometheus.app.src | 2 +- .../src/emqx_prometheus_app.erl | 4 +-- .../src/emqx_prometheus_schema.erl | 30 +++++++++++++++++++ 6 files changed, 40 insertions(+), 49 deletions(-) delete mode 100644 apps/emqx_prometheus/priv/emqx_prometheus.schema create mode 100644 apps/emqx_prometheus/src/emqx_prometheus_schema.erl diff --git a/apps/emqx/src/emqx_schema.erl b/apps/emqx/src/emqx_schema.erl index 4570528c2..8c1967ed3 100644 --- a/apps/emqx/src/emqx_schema.erl +++ b/apps/emqx/src/emqx_schema.erl @@ -55,7 +55,7 @@ structs() -> ["cluster", "node", "rpc", "log", "lager", "acl", "mqtt", "zone", "listener", "module", "broker", - "plugins", "sysmon", "os_mon", "vm_mon", "alarm", "telemetry"] + "plugins", "sysmon", "os_mon", "vm_mon", "alarm"] ++ includes(). -ifdef(TEST). @@ -477,12 +477,6 @@ fields("alarm") -> , {"validity_period", t(duration_s(), undefined, "24h")} ]; -fields("telemetry") -> - [ {"enabled", t(boolean(), undefined, false)} - , {"url", t(string(), undefined, "https://telemetry-emqx-io.bigpar.vercel.app/api/telemetry")} - , {"report_interval", t(duration_s(), undefined, "7d")} - ]; - fields(ExtraField) -> Mod = list_to_atom(ExtraField++"_schema"), Mod:fields(ExtraField). @@ -513,7 +507,6 @@ translation("emqx") -> , {"os_mon", fun tr_os_mon/1} , {"vm_mon", fun tr_vm_mon/1} , {"alarm", fun tr_alarm/1} - , {"telemetry", fun tr_telemetry/1} ]. tr_cluster__discovery(Conf) -> @@ -668,7 +661,7 @@ tr_zones(Conf) -> tr_listeners(Conf) -> Atom = fun(undefined) -> undefined; - (B) when is_binary(B)-> binary_to_atom(B); + (B) when is_binary(B)-> binary_to_atom(B, utf8); (S) when is_list(S) -> list_to_atom(S) end, Access = fun(S) -> @@ -836,7 +829,7 @@ tr_modules(Conf) -> tr_sysmon(Conf) -> Keys = maps:to_list(conf_get("sysmon", Conf, #{})), - [{binary_to_atom(K), maps:get(value, V)} || {K, V} <- Keys]. + [{binary_to_atom(K, utf8), maps:get(value, V)} || {K, V} <- Keys]. tr_os_mon(Conf) -> [{cpu_check_interval, conf_get("os_mon.cpu_check_interval", Conf)} @@ -859,12 +852,6 @@ tr_alarm(Conf) -> , {validity_period, conf_get("alarm.validity_period", Conf)} ]. -tr_telemetry(Conf) -> - [ {enabled, conf_get("telemetry.enabled", Conf)} - , {url, conf_get("telemetry.url", Conf)} - , {report_interval, conf_get("telemetry.report_interval", Conf)} - ]. - %% helpers options(static, Conf) -> diff --git a/apps/emqx_prometheus/etc/emqx_prometheus.conf b/apps/emqx_prometheus/etc/emqx_prometheus.conf index 92e0d850d..c450846fe 100644 --- a/apps/emqx_prometheus/etc/emqx_prometheus.conf +++ b/apps/emqx_prometheus/etc/emqx_prometheus.conf @@ -1,13 +1,7 @@ ##-------------------------------------------------------------------- ## emqx_prometheus for EMQ X ##-------------------------------------------------------------------- - -## The Prometheus Push Gateway URL address -## -## Note: You can comment out this line to disable it -prometheus.push.gateway.server = "http://127.0.0.1:9091" - -## The metrics data push interval (millisecond) -## -## Default: 15000 -prometheus.interval = 15000 +emqx_prometheus:{ + push_gateway_server: "http://127.0.0.1:9091" + interval: "15s" +} diff --git a/apps/emqx_prometheus/priv/emqx_prometheus.schema b/apps/emqx_prometheus/priv/emqx_prometheus.schema deleted file mode 100644 index d946813c5..000000000 --- a/apps/emqx_prometheus/priv/emqx_prometheus.schema +++ /dev/null @@ -1,20 +0,0 @@ -%% -*-: erlang -*- -%% emqx_prometheus config - -{mapping, "prometheus.push.gateway.server", "emqx_prometheus.push_gateway", [ - {datatype, string} -]}. - -{mapping, "prometheus.interval", "emqx_prometheus.interval", [ - {default, 5000}, - {datatype, integer} -]}. - -{mapping, "prometheus.collector.$name", "prometheus.collectors", [ - {datatype, atom} -]}. - -{translation, "prometheus.collectors", fun(Conf) -> - Collectors = cuttlefish_variable:filter_by_prefix("prometheus.collector", Conf), - lists:map(fun({_, Collector}) -> Collector end, Collectors) -end}. diff --git a/apps/emqx_prometheus/src/emqx_prometheus.app.src b/apps/emqx_prometheus/src/emqx_prometheus.app.src index b96608edb..62105ff0b 100644 --- a/apps/emqx_prometheus/src/emqx_prometheus.app.src +++ b/apps/emqx_prometheus/src/emqx_prometheus.app.src @@ -1,6 +1,6 @@ {application, emqx_prometheus, [{description, "Prometheus for EMQ X"}, - {vsn, "4.3.0"}, % strict semver, bump manually! + {vsn, "5.0.0"}, % strict semver, bump manually! {modules, []}, {registered, [emqx_prometheus_sup]}, {applications, [kernel,stdlib,prometheus]}, diff --git a/apps/emqx_prometheus/src/emqx_prometheus_app.erl b/apps/emqx_prometheus/src/emqx_prometheus_app.erl index df0701551..9024bd583 100644 --- a/apps/emqx_prometheus/src/emqx_prometheus_app.erl +++ b/apps/emqx_prometheus/src/emqx_prometheus_app.erl @@ -28,8 +28,8 @@ -define(APP, emqx_prometheus). start(_StartType, _StartArgs) -> - PushGateway = application:get_env(?APP, push_gateway, undefined), - Interval = application:get_env(?APP, interval, 5000), + PushGateway = emqx_config:get([?APP, push_gateway_server], undefined), + Interval = emqx_config:get([?APP, interval], 15000), emqx_prometheus_sup:start_link(PushGateway, Interval). stop(_State) -> diff --git a/apps/emqx_prometheus/src/emqx_prometheus_schema.erl b/apps/emqx_prometheus/src/emqx_prometheus_schema.erl new file mode 100644 index 000000000..486362e7b --- /dev/null +++ b/apps/emqx_prometheus/src/emqx_prometheus_schema.erl @@ -0,0 +1,30 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- +-module(emqx_prometheus_schema). + +-include_lib("typerefl/include/types.hrl"). + +-behaviour(hocon_schema). + +-export([ structs/0 + , fields/1]). + +structs() -> ["emqx_prometheus"]. + +fields("emqx_prometheus") -> + [ {push_gateway_server, emqx_schema:t(string())} + , {interval, emqx_schema:t(emqx_schema:duration_ms(), undefined, "15s")} + ]. From c9c6b69cc9cf0d3221e7c784aeef20104deb4110 Mon Sep 17 00:00:00 2001 From: turtleDeng Date: Fri, 25 Jun 2021 17:06:30 +0800 Subject: [PATCH 025/174] chore: reset plugin conf generate (#5094) * chore: reset plugin conf generate * fix(plugin): check dialyzer fail * chore: rm emqx_management_schema.erl file * fix(plugin): check dialyzer fail * fix(plugin): fix check ct fail * fix(plugin): check dialyzer fail --- apps/emqx/src/emqx_config.erl | 1 + apps/emqx/src/emqx_plugins.erl | 86 +++++++++++++++++++++++++++++++++- 2 files changed, 86 insertions(+), 1 deletion(-) diff --git a/apps/emqx/src/emqx_config.erl b/apps/emqx/src/emqx_config.erl index 3d431f6a8..213eb7a8b 100644 --- a/apps/emqx/src/emqx_config.erl +++ b/apps/emqx/src/emqx_config.erl @@ -97,6 +97,7 @@ put_raw(KeyPath, Config) -> put_raw(deep_put(KeyPath, get_raw(), Config)). %%----------------------------------------------------------------- +-dialyzer([{nowarn_function, [deep_get/2]}]). -spec deep_get(config_key_path(), map()) -> term(). deep_get(ConfKeyPath, Map) -> do_deep_get(ConfKeyPath, Map, fun(KeyPath, Data) -> diff --git a/apps/emqx/src/emqx_plugins.erl b/apps/emqx/src/emqx_plugins.erl index 2f39edcb4..8abc2b21f 100644 --- a/apps/emqx/src/emqx_plugins.erl +++ b/apps/emqx/src/emqx_plugins.erl @@ -30,6 +30,8 @@ , reload/1 , list/0 , find_plugin/1 + , generate_configs/1 + , apply_configs/1 ]). -export([funlog/2]). @@ -171,7 +173,15 @@ load_ext_plugin(PluginDir) -> ?LOG(alert, "plugin_app_file_not_found: ~s", [AppFile]), error({plugin_app_file_not_found, AppFile}) end, - load_plugin_app(AppName, Ebin). + ok = load_plugin_app(AppName, Ebin), + try + ok = generate_configs(AppName, PluginDir) + catch + throw : {conf_file_not_found, ConfFile} -> + %% this is maybe a dependency of an external plugin + ?LOG(debug, "config_load_error_ignored for app=~p, path=~s", [AppName, ConfFile]), + ok + end. load_plugin_app(AppName, Ebin) -> _ = code:add_patha(Ebin), @@ -236,6 +246,7 @@ plugin(AppName, Type) -> load_plugin(Name, Persistent) -> try + ok = ?MODULE:generate_configs(Name), case load_app(Name) of ok -> start_app(Name, fun(App) -> plugin_loaded(App, Persistent) end); @@ -351,5 +362,78 @@ plugin_type(backend) -> backend; plugin_type(bridge) -> bridge; plugin_type(_) -> feature. + funlog(Key, Value) -> ?LOG(info, "~s = ~p", [string:join(Key, "."), Value]). + +generate_configs(App) -> + PluginConfDir = emqx:get_env(plugins_etc_dir), + PluginSchemaDir = code:priv_dir(App), + generate_configs(App, PluginConfDir, PluginSchemaDir). + +generate_configs(App, PluginDir) -> + PluginConfDir = filename:join([PluginDir, "etc"]), + PluginSchemaDir = filename:join([PluginDir, "priv"]), + generate_configs(App, PluginConfDir, PluginSchemaDir). + +generate_configs(App, PluginConfDir, PluginSchemaDir) -> + ConfigFile = filename:join([PluginConfDir, App]) ++ ".config", + case filelib:is_file(ConfigFile) of + true -> + {ok, [Configs]} = file:consult(ConfigFile), + apply_configs(Configs); + false -> + SchemaFile = filename:join([PluginSchemaDir, App]) ++ ".schema", + case filelib:is_file(SchemaFile) of + true -> + AppsEnv = do_generate_configs(App), + apply_configs(AppsEnv); + false -> + SchemaMod = lists:concat([App, "_schema"]), + ConfName = filename:join([PluginConfDir, App]) ++ ".conf", + SchemaFile1 = filename:join([code:lib_dir(App), "ebin", SchemaMod]) ++ ".beam", + do_generate_hocon_configs(App, ConfName, SchemaFile1) + end + end. + +do_generate_configs(App) -> + Name1 = filename:join([emqx:get_env(plugins_etc_dir), App]) ++ ".conf", + Name2 = filename:join([code:lib_dir(App), "etc", App]) ++ ".conf", + ConfFile = case {filelib:is_file(Name1), filelib:is_file(Name2)} of + {true, _} -> Name1; + {false, true} -> Name2; + {false, false} -> error({config_not_found, [Name1, Name2]}) + end, + SchemaFile = filename:join([code:priv_dir(App), App]) ++ ".schema", + case filelib:is_file(SchemaFile) of + true -> + Schema = cuttlefish_schema:files([SchemaFile]), + Conf = cuttlefish_conf:file(ConfFile), + cuttlefish_generator:map(Schema, Conf, undefined, fun ?MODULE:funlog/2); + false -> + error({schema_not_found, SchemaFile}) + end. + +do_generate_hocon_configs(App, ConfName, SchemaFile) -> + SchemaMod = lists:concat([App, "_schema"]), + case {filelib:is_file(ConfName), filelib:is_file(SchemaFile)} of + {true, true} -> + {ok, RawConfig} = hocon:load(ConfName, #{format => richmap}), + _ = hocon_schema:check(list_to_atom(SchemaMod), RawConfig, #{atom_key => true, + return_plain => true}), + ok; + % emqx_config:update_config([App], Config); + {true, false} -> + error({schema_not_found, [SchemaFile]}); + {false, true} -> + error({config_not_found, [ConfName]}); + {false, false} -> + error({conf_and_schema_not_found, [ConfName, SchemaFile]}) + end. + +apply_configs([]) -> + ok; +apply_configs([{App, Config} | More]) -> + lists:foreach(fun({Key, _}) -> application:unset_env(App, Key) end, application:get_all_env(App)), + lists:foreach(fun({Key, Val}) -> application:set_env(App, Key, Val) end, Config), + apply_configs(More). From 2d1008ceaf78d9ad3f661568f76964f5117c0dc2 Mon Sep 17 00:00:00 2001 From: Turtle Date: Fri, 25 Jun 2021 15:10:45 +0800 Subject: [PATCH 026/174] chore: move emqx_recon to emqx_modules plugin --- .../src/emqx_mod_recon.erl} | 30 ++++- apps/emqx_recon/.gitignore | 31 ----- apps/emqx_recon/README.md | 61 ---------- apps/emqx_recon/etc/emqx_recon.conf | 4 - apps/emqx_recon/priv/emqx_recon.schema | 9 -- apps/emqx_recon/rebar.config | 18 --- apps/emqx_recon/src/emqx_recon.app.src | 14 --- apps/emqx_recon/src/emqx_recon.erl | 41 ------- apps/emqx_recon/test/emqx_recon_SUITE.erl | 114 ------------------ data/loaded_modules.tmpl | 1 + data/loaded_plugins.tmpl | 1 - rebar.config.erl | 2 - 12 files changed, 26 insertions(+), 300 deletions(-) rename apps/{emqx_recon/src/emqx_recon_cli.erl => emqx_modules/src/emqx_mod_recon.erl} (85%) delete mode 100644 apps/emqx_recon/.gitignore delete mode 100644 apps/emqx_recon/README.md delete mode 100644 apps/emqx_recon/etc/emqx_recon.conf delete mode 100644 apps/emqx_recon/priv/emqx_recon.schema delete mode 100644 apps/emqx_recon/rebar.config delete mode 100644 apps/emqx_recon/src/emqx_recon.app.src delete mode 100644 apps/emqx_recon/src/emqx_recon.erl delete mode 100644 apps/emqx_recon/test/emqx_recon_SUITE.erl diff --git a/apps/emqx_recon/src/emqx_recon_cli.erl b/apps/emqx_modules/src/emqx_mod_recon.erl similarity index 85% rename from apps/emqx_recon/src/emqx_recon_cli.erl rename to apps/emqx_modules/src/emqx_mod_recon.erl index b4e3a52ab..38d046b10 100644 --- a/apps/emqx_recon/src/emqx_recon_cli.erl +++ b/apps/emqx_modules/src/emqx_mod_recon.erl @@ -14,13 +14,34 @@ %% limitations under the License. %%-------------------------------------------------------------------- --module(emqx_recon_cli). +-module(emqx_mod_recon). --export([ cmd/1 - , load/0 - , unload/0 +-behaviour(emqx_gen_mod). + +%% emqx_gen_mod callbacks +-export([ load/1 + , unload/1 + , description/0 ]). +-export([cmd/1]). + + +%%-------------------------------------------------------------------- +%% Load/Unload +%%-------------------------------------------------------------------- + +-spec(load(list()) -> ok). +load(_Env) -> + load(). + +-spec(unload(list()) -> ok). +unload(_Env) -> + unload(). + +description() -> + "EMQ X Recon Module". + load() -> emqx_ctl:register_command(recon, {?MODULE, cmd}, []). @@ -69,4 +90,3 @@ remote_load(Module) -> remote_load(nodes(), Module). %% after OTP 23, it crashes with 'badarg' error remote_load([], _Module) -> ok; remote_load(Nodes, Module) -> recon:remote_load(Nodes, Module). - diff --git a/apps/emqx_recon/.gitignore b/apps/emqx_recon/.gitignore deleted file mode 100644 index 3576f6e23..000000000 --- a/apps/emqx_recon/.gitignore +++ /dev/null @@ -1,31 +0,0 @@ -.rebar -.eunit -deps -!deps/.placeholder -*.o -*.beam -*.plt -erl_crash.dump -ebin -!ebin/.placeholder -.exrc -log/ -*.swp -*.so -.erlang.mk/ -logs/ -ct.coverdata -test/ct.cover.spec -.idea/ -emqx_recon.iml -Mnesia.nonode@nohost/ -data/ -cover/ -emqx_recon.d -eunit.coverdata -.DS_Store -_build/ -rebar.lock -erlang.mk -rebar3.crashdump -.rebar3/ diff --git a/apps/emqx_recon/README.md b/apps/emqx_recon/README.md deleted file mode 100644 index ca163be45..000000000 --- a/apps/emqx_recon/README.md +++ /dev/null @@ -1,61 +0,0 @@ - -emqx-recon -========== - -EMQ X Recon Debug/Optimize Plugin - -emqx_recon.conf -----=---------- - -``` -## Global GC Interval. -## -## Value: Duration -## - h: hour -## - m: minute -## - s: second -## -## Examples: -## - 2h: 2 hours -## - 30m: 30 minutes -## - 20s: 20 seconds -## -## Defaut: 5m -recon.gc_interval = 5m -``` - -Load the Plugin ---------------- - -``` -./bin/emqx_ctl plugins load emqx_recon -``` - -Commands --------- - -``` -./bin/emqx_ctl recon - -recon memory #recon_alloc:memory/2 -recon allocated #recon_alloc:memory(allocated_types, current|max) -recon bin_leak #recon:bin_leak(100) -recon node_stats #recon:node_stats(10, 1000) -recon remote_load Mod #recon:remote_load(Mod) -``` - -GC --- - -When the plugin is loaded, global GC will run periodically. - -License -------- - -Apache License Version 2.0 - -Author ------- - -EMQ X Team. - diff --git a/apps/emqx_recon/etc/emqx_recon.conf b/apps/emqx_recon/etc/emqx_recon.conf deleted file mode 100644 index 1ca23bfc2..000000000 --- a/apps/emqx_recon/etc/emqx_recon.conf +++ /dev/null @@ -1,4 +0,0 @@ -##-------------------------------------------------------------------- -## Recon Plugin -##-------------------------------------------------------------------- - diff --git a/apps/emqx_recon/priv/emqx_recon.schema b/apps/emqx_recon/priv/emqx_recon.schema deleted file mode 100644 index 7436d0f8c..000000000 --- a/apps/emqx_recon/priv/emqx_recon.schema +++ /dev/null @@ -1,9 +0,0 @@ -%%-*- mode: erlang -*- -%% emqx-recon config mapping - -%% @doc Global GC Interval -%% {@configurable} -%% {mapping, "recon.gc_interval", "emqx_recon.gc_interval", [ -%% {datatype, {duration, s}} -%% ]}. - diff --git a/apps/emqx_recon/rebar.config b/apps/emqx_recon/rebar.config deleted file mode 100644 index 1a6f20d2b..000000000 --- a/apps/emqx_recon/rebar.config +++ /dev/null @@ -1,18 +0,0 @@ -{deps, [ -%% recon "https://github.com/ferd/recon" at root rebar.config -]}. - -{edoc_opts, [{preprocess, true}]}. -{erl_opts, [warn_unused_vars, - warn_shadow_vars, - warn_unused_import, - warn_obsolete_guard, - debug_info - ]}. - -{xref_checks, [undefined_function_calls, undefined_functions, - locals_not_used, deprecated_function_calls, - warnings_as_errors, deprecated_functions]}. -{cover_enabled, true}. -{cover_opts, [verbose]}. -{cover_export_enabled, true}. diff --git a/apps/emqx_recon/src/emqx_recon.app.src b/apps/emqx_recon/src/emqx_recon.app.src deleted file mode 100644 index e25e2bbc5..000000000 --- a/apps/emqx_recon/src/emqx_recon.app.src +++ /dev/null @@ -1,14 +0,0 @@ -{application, emqx_recon, - [{description, "EMQ X Recon Plugin"}, - {vsn, "4.4.0"}, % strict semver, bump manually! - {modules, []}, - {registered, []}, - {applications, [kernel,stdlib,recon]}, - {mod, {emqx_recon,[]}}, - {env, []}, - {licenses, ["Apache-2.0"]}, - {maintainers, ["EMQ X Team "]}, - {links, [{"Homepage", "https://emqx.io/"}, - {"Github", "https://github.com/emqx/emqx-recon"} - ]} - ]}. diff --git a/apps/emqx_recon/src/emqx_recon.erl b/apps/emqx_recon/src/emqx_recon.erl deleted file mode 100644 index 67039c3ac..000000000 --- a/apps/emqx_recon/src/emqx_recon.erl +++ /dev/null @@ -1,41 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_recon). - --emqx_plugin(?MODULE). - --behaviour(application). - --export([start/2, stop/1]). - --behaviour(supervisor). - --export([init/1]). - --define(APP, ?MODULE). --define(SUP, emqx_recon_sup). - -start(_StartType, _StartArgs) -> - emqx_recon_cli:load(), - supervisor:start_link({local, ?SUP}, ?MODULE, []). - -stop(_State) -> - emqx_recon_cli:unload(). - -init([]) -> - {ok, {{one_for_one, 10, 100}, []}}. - diff --git a/apps/emqx_recon/test/emqx_recon_SUITE.erl b/apps/emqx_recon/test/emqx_recon_SUITE.erl deleted file mode 100644 index 2abf8f709..000000000 --- a/apps/emqx_recon/test/emqx_recon_SUITE.erl +++ /dev/null @@ -1,114 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_recon_SUITE). - --compile(nowarn_export_all). --compile(export_all). - --include_lib("eunit/include/eunit.hrl"). - --define(output_patterns(V), ((V)++"")). - -all() -> [{group, cli}]. - -groups() -> - [{cli, [sequence], - [cli_memory, - cli_allocated, - cli_bin_leak, - cli_node_stats, - cli_remote_load, - cli_usage]} - ]. - -init_per_suite(Config) -> - emqx_ct_helpers:start_apps([emqx_recon]), - Config. - -end_per_suite(_Config) -> - emqx_ct_helpers:stop_apps([emqx_recon]). - -cli_memory(_) -> - mock_print(), - Output = emqx_recon_cli:cmd(["memory"]), - Zip = lists:zip(Output, [ ?output_patterns("usage/current") - , ?output_patterns("usage/max") - , ?output_patterns("used/current") - , ?output_patterns("used/max") - , ?output_patterns("allocated/current") - , ?output_patterns("allocated/max") - , ?output_patterns("unused/current") - , ?output_patterns("unused/max") - ]), - %ct:pal("=======~p", [Zip]), - [?assertMatch({match, _}, re:run(Line, Match, [{capture,all,list}])) - || {Line, Match} <- Zip], - unmock_print(). - -cli_allocated(_) -> - mock_print(), - Output = emqx_recon_cli:cmd(["allocated"]), - Zip = lists:zip(Output, [ ?output_patterns("binary_alloc/current") - , ?output_patterns("driver_alloc/current") - , ?output_patterns("eheap_alloc/current") - , ?output_patterns("ets_alloc/current") - , ?output_patterns("fix_alloc/current") - , ?output_patterns("ll_alloc/current") - , ?output_patterns("sl_alloc/current") - , ?output_patterns("std_alloc/current") - , ?output_patterns("temp_alloc/current") - , ?output_patterns("binary_alloc/max") - , ?output_patterns("driver_alloc/max") - , ?output_patterns("eheap_alloc/max") - , ?output_patterns("ets_alloc/max") - , ?output_patterns("fix_alloc/max") - , ?output_patterns("ll_alloc/max") - , ?output_patterns("sl_alloc/max") - , ?output_patterns("std_alloc/max") - , ?output_patterns("temp_alloc/max") - ]), - ct:pal("=======~p", [Zip]), - [?assertMatch({match, _}, re:run(Line, Match, [{capture,all,list}])) - || {Line, Match} <- Zip], - unmock_print(). - -cli_bin_leak(_) -> - mock_print(), - Output = emqx_recon_cli:cmd(["bin_leak"]), - [?assertMatch({match, _}, re:run(Line, "current_function", [{capture,all,list}])) - || Line <- Output], - unmock_print(). - -cli_node_stats(_) -> - emqx_recon_cli:cmd(["node_stats"]). - -cli_remote_load(_) -> - emqx_recon_cli:cmd(["remote_load", "emqx_recon_cli"]). - -cli_usage(_) -> - emqx_recon_cli:cmd(["usage"]). - -mock_print() -> - catch meck:unload(emqx_ctl), - meck:new(emqx_ctl, [non_strict, passthrough]), - meck:expect(emqx_ctl, print, fun(Arg) -> emqx_ctl:format(Arg) end), - meck:expect(emqx_ctl, print, fun(Msg, Arg) -> emqx_ctl:format(Msg, Arg) end), - meck:expect(emqx_ctl, usage, fun(Usages) -> emqx_ctl:format_usage(Usages) end). - -unmock_print() -> - meck:unload(). - diff --git a/data/loaded_modules.tmpl b/data/loaded_modules.tmpl index f31e47900..8dfe6453e 100644 --- a/data/loaded_modules.tmpl +++ b/data/loaded_modules.tmpl @@ -1 +1,2 @@ {emqx_mod_presence, true}. +{emqx_mod_recon, true}. diff --git a/data/loaded_plugins.tmpl b/data/loaded_plugins.tmpl index 5922f6dc5..c2b6311f2 100644 --- a/data/loaded_plugins.tmpl +++ b/data/loaded_plugins.tmpl @@ -1,7 +1,6 @@ {emqx_management, true}. {emqx_dashboard, true}. {emqx_modules, {{enable_plugin_emqx_modules}}}. -{emqx_recon, {{enable_plugin_emqx_recon}}}. {emqx_retainer, {{enable_plugin_emqx_retainer}}}. {emqx_rule_engine, {{enable_plugin_emqx_rule_engine}}}. {emqx_bridge_mqtt, {{enable_plugin_emqx_bridge_mqtt}}}. diff --git a/rebar.config.erl b/rebar.config.erl index 8ce3aa217..88148818e 100644 --- a/rebar.config.erl +++ b/rebar.config.erl @@ -188,7 +188,6 @@ overlay_vars_rel(RelType) -> [ {enable_plugin_emqx_rule_engine, RelType =:= cloud} , {enable_plugin_emqx_bridge_mqtt, RelType =:= edge} , {enable_plugin_emqx_modules, false} %% modules is not a plugin in ce - , {enable_plugin_emqx_recon, true} , {enable_plugin_emqx_retainer, true} , {vm_args_file, VmArgs} ]. @@ -285,7 +284,6 @@ relx_plugin_apps(ReleaseType) -> , emqx_auth_jwt , emqx_auth_mnesia , emqx_web_hook - , emqx_recon , emqx_rule_engine , emqx_sasl , emqx_statsd From c24f3688c4eec0b16e2aef4f1d2fbbc5dcde9d4f Mon Sep 17 00:00:00 2001 From: zhanghongtong Date: Fri, 25 Jun 2021 09:41:45 +0800 Subject: [PATCH 027/174] build: delete needless auth plugins --- apps/emqx_auth_http/.gitignore | 25 -- apps/emqx_auth_http/README.md | 100 ----- apps/emqx_auth_http/etc/emqx_auth_http.conf | 172 --------- .../emqx_auth_http/include/emqx_auth_http.hrl | 23 -- .../emqx_auth_http/priv/emqx_auth_http.schema | 131 ------- apps/emqx_auth_http/rebar.config | 26 -- apps/emqx_auth_http/src/emqx_acl_http.erl | 88 ----- .../emqx_auth_http/src/emqx_auth_http.app.src | 14 - apps/emqx_auth_http/src/emqx_auth_http.erl | 112 ------ .../emqx_auth_http/src/emqx_auth_http_app.erl | 158 -------- .../emqx_auth_http/src/emqx_auth_http_cli.erl | 92 ----- .../emqx_auth_http/src/emqx_auth_http_sup.erl | 29 -- .../test/emqx_auth_http_SUITE.erl | 257 ------------- apps/emqx_auth_http/test/http_auth_server.erl | 152 -------- apps/emqx_auth_jwt/.gitignore | 28 -- apps/emqx_auth_jwt/README.md | 90 ----- apps/emqx_auth_jwt/TODO.md | 2 - .../doc/hmac-vs-ecdsa-for-jwt.txt | 3 - apps/emqx_auth_jwt/etc/emqx_auth_jwt.conf | 49 --- apps/emqx_auth_jwt/priv/emqx_auth_jwt.schema | 49 --- apps/emqx_auth_jwt/rebar.config | 25 -- apps/emqx_auth_jwt/src/emqx_auth_jwt.app.src | 14 - .../emqx_auth_jwt/src/emqx_auth_jwt.appup.src | 15 - apps/emqx_auth_jwt/src/emqx_auth_jwt.erl | 99 ----- apps/emqx_auth_jwt/src/emqx_auth_jwt_app.erl | 81 ----- apps/emqx_auth_jwt/src/emqx_auth_jwt_svr.erl | 224 ------------ .../test/emqx_auth_jwt_SUITE.erl | 166 --------- apps/emqx_auth_ldap/.gitignore | 25 -- apps/emqx_auth_ldap/README.md | 96 ----- apps/emqx_auth_ldap/emqx.io.ldif | 135 ------- apps/emqx_auth_ldap/emqx.schema | 46 --- apps/emqx_auth_ldap/etc/emqx_auth_ldap.conf | 76 ---- .../emqx_auth_ldap/include/emqx_auth_ldap.hrl | 23 -- .../emqx_auth_ldap/priv/emqx_auth_ldap.schema | 174 --------- apps/emqx_auth_ldap/rebar.config | 25 -- apps/emqx_auth_ldap/src/emqx_acl_ldap.erl | 98 ----- .../emqx_auth_ldap/src/emqx_auth_ldap.app.src | 14 - apps/emqx_auth_ldap/src/emqx_auth_ldap.erl | 210 ----------- .../emqx_auth_ldap/src/emqx_auth_ldap_app.erl | 78 ---- .../emqx_auth_ldap/src/emqx_auth_ldap_cli.erl | 150 -------- .../emqx_auth_ldap/src/emqx_auth_ldap_sup.erl | 35 -- apps/emqx_auth_ldap/test/certs/cacert.pem | 20 - apps/emqx_auth_ldap/test/certs/cert.pem | 19 - .../emqx_auth_ldap/test/certs/client-cert.pem | 19 - apps/emqx_auth_ldap/test/certs/client-key.pem | 27 -- apps/emqx_auth_ldap/test/certs/key.pem | 27 -- .../test/emqx_auth_ldap_SUITE.erl | 152 -------- .../emqx_auth_ldap_bind_as_user_SUITE.erl | 112 ------ apps/emqx_auth_mnesia/.gitignore | 26 -- apps/emqx_auth_mnesia/README.md | 2 - .../etc/emqx_auth_mnesia.conf | 30 -- .../include/emqx_auth_mnesia.hrl | 38 -- .../priv/emqx_auth_mnesia.schema | 43 --- apps/emqx_auth_mnesia/rebar.config | 17 - apps/emqx_auth_mnesia/src/emqx_acl_mnesia.erl | 104 ------ .../src/emqx_acl_mnesia_api.erl | 226 ------------ .../src/emqx_acl_mnesia_cli.erl | 270 -------------- .../src/emqx_auth_mnesia.app.src | 14 - .../src/emqx_auth_mnesia.appup.src | 13 - .../emqx_auth_mnesia/src/emqx_auth_mnesia.erl | 109 ------ .../src/emqx_auth_mnesia_api.erl | 305 ---------------- .../src/emqx_auth_mnesia_app.erl | 68 ---- .../src/emqx_auth_mnesia_cli.erl | 194 ---------- .../src/emqx_auth_mnesia_sup.erl | 36 -- .../test/emqx_acl_mnesia_SUITE.erl | 293 --------------- .../test/emqx_auth_mnesia_SUITE.erl | 318 ---------------- apps/emqx_auth_mongo/.gitignore | 24 -- apps/emqx_auth_mongo/CHANGES | 31 -- apps/emqx_auth_mongo/README.md | 192 ---------- apps/emqx_auth_mongo/etc/emqx_auth_mongo.conf | 187 ---------- .../include/emqx_auth_mongo.hrl | 37 -- .../priv/emqx_auth_mongo.schema | 341 ------------------ apps/emqx_auth_mongo/rebar.config | 32 -- apps/emqx_auth_mongo/src/emqx_acl_mongo.erl | 91 ----- .../src/emqx_auth_mongo.app.src | 14 - apps/emqx_auth_mongo/src/emqx_auth_mongo.erl | 138 ------- .../src/emqx_auth_mongo_app.erl | 88 ----- .../src/emqx_auth_mongo_sup.erl | 34 -- .../test/emqx_auth_mongo_SUITE.erl | 169 --------- .../emqx_auth_mongo_SUITE_data/ca-key.pem | 27 -- .../test/emqx_auth_mongo_SUITE_data/ca.pem | 19 - .../client-cert.pem | 19 - .../emqx_auth_mongo_SUITE_data/client-key.pem | 27 -- .../emqx_auth_mongo_SUITE_data/mongodb.pem | 46 --- .../private_key.pem | 27 -- .../emqx_auth_mongo_SUITE_data/public_key.pem | 9 - .../server-cert.pem | 19 - .../emqx_auth_mongo_SUITE_data/server-key.pem | 27 -- apps/emqx_auth_mysql/.gitignore | 31 -- apps/emqx_auth_mysql/README.md | 167 --------- apps/emqx_auth_mysql/etc/emqx_auth_mysql.conf | 131 ------- .../include/emqx_auth_mysql.hrl | 23 -- apps/emqx_auth_mysql/mqtt.sql | 41 --- .../priv/emqx_auth_mysql.schema | 156 -------- apps/emqx_auth_mysql/rebar.config | 24 -- apps/emqx_auth_mysql/src/emqx_acl_mysql.erl | 119 ------ .../src/emqx_auth_mysql.app.src | 14 - apps/emqx_auth_mysql/src/emqx_auth_mysql.erl | 91 ----- .../src/emqx_auth_mysql_app.erl | 74 ---- .../src/emqx_auth_mysql_cli.erl | 91 ----- .../src/emqx_auth_mysql_sup.erl | 40 -- .../test/emqx_auth_mysql_SUITE.erl | 235 ------------ .../emqx_auth_mysql_SUITE_data/ca-key.pem | 27 -- .../test/emqx_auth_mysql_SUITE_data/ca.pem | 19 - .../client-cert.pem | 19 - .../emqx_auth_mysql_SUITE_data/client-key.pem | 27 -- .../private_key.pem | 27 -- .../emqx_auth_mysql_SUITE_data/public_key.pem | 9 - .../server-cert.pem | 19 - .../emqx_auth_mysql_SUITE_data/server-key.pem | 27 -- apps/emqx_auth_pgsql/.gitignore | 20 - apps/emqx_auth_pgsql/README.md | 183 ---------- apps/emqx_auth_pgsql/etc/emqx_auth_pgsql.conf | 132 ------- .../include/emqx_auth_pgsql.hrl | 23 -- apps/emqx_auth_pgsql/mqtt.sql | 28 -- .../priv/emqx_auth_pgsql.schema | 184 ---------- apps/emqx_auth_pgsql/rebar.config | 21 -- apps/emqx_auth_pgsql/src/emqx_acl_pgsql.erl | 117 ------ .../src/emqx_auth_pgsql.app.src | 14 - apps/emqx_auth_pgsql/src/emqx_auth_pgsql.erl | 91 ----- .../src/emqx_auth_pgsql_app.erl | 63 ---- .../src/emqx_auth_pgsql_cli.erl | 150 -------- .../src/emqx_auth_pgsql_sup.erl | 37 -- .../test/emqx_auth_pgsql_SUITE.erl | 221 ------------ .../emqx_auth_pgsql_SUITE_data/ca-key.pem | 27 -- .../test/emqx_auth_pgsql_SUITE_data/ca.pem | 19 - .../client-cert.pem | 19 - .../emqx_auth_pgsql_SUITE_data/client-key.pem | 27 -- .../private_key.pem | 27 -- .../emqx_auth_pgsql_SUITE_data/public_key.pem | 9 - .../server-cert.pem | 19 - .../emqx_auth_pgsql_SUITE_data/server-key.pem | 27 -- apps/emqx_auth_redis/.gitignore | 28 -- apps/emqx_auth_redis/README.md | 171 --------- apps/emqx_auth_redis/etc/emqx_auth_redis.conf | 131 ------- .../include/emqx_auth_redis.hrl | 23 -- .../priv/emqx_auth_redis.schema | 184 ---------- apps/emqx_auth_redis/rebar.config | 24 -- apps/emqx_auth_redis/src/emqx_acl_redis.erl | 86 ----- .../src/emqx_auth_redis.app.src | 14 - apps/emqx_auth_redis/src/emqx_auth_redis.erl | 85 ----- .../src/emqx_auth_redis_app.erl | 70 ---- .../src/emqx_auth_redis_cli.erl | 89 ----- .../src/emqx_auth_redis_sup.erl | 43 --- .../test/emqx_auth_redis_SUITE.erl | 186 ---------- .../emqx_auth_redis_SUITE_data/certs/ca.crt | 29 -- .../emqx_auth_redis_SUITE_data/certs/ca.key | 51 --- .../emqx_auth_redis_SUITE_data/certs/ca.txt | 1 - .../certs/redis.crt | 23 -- .../emqx_auth_redis_SUITE_data/certs/redis.dh | 8 - .../certs/redis.key | 27 -- 151 files changed, 11631 deletions(-) delete mode 100644 apps/emqx_auth_http/.gitignore delete mode 100644 apps/emqx_auth_http/README.md delete mode 100644 apps/emqx_auth_http/etc/emqx_auth_http.conf delete mode 100644 apps/emqx_auth_http/include/emqx_auth_http.hrl delete mode 100644 apps/emqx_auth_http/priv/emqx_auth_http.schema delete mode 100644 apps/emqx_auth_http/rebar.config delete mode 100644 apps/emqx_auth_http/src/emqx_acl_http.erl delete mode 100644 apps/emqx_auth_http/src/emqx_auth_http.app.src delete mode 100644 apps/emqx_auth_http/src/emqx_auth_http.erl delete mode 100644 apps/emqx_auth_http/src/emqx_auth_http_app.erl delete mode 100644 apps/emqx_auth_http/src/emqx_auth_http_cli.erl delete mode 100644 apps/emqx_auth_http/src/emqx_auth_http_sup.erl delete mode 100644 apps/emqx_auth_http/test/emqx_auth_http_SUITE.erl delete mode 100644 apps/emqx_auth_http/test/http_auth_server.erl delete mode 100644 apps/emqx_auth_jwt/.gitignore delete mode 100644 apps/emqx_auth_jwt/README.md delete mode 100644 apps/emqx_auth_jwt/TODO.md delete mode 100644 apps/emqx_auth_jwt/doc/hmac-vs-ecdsa-for-jwt.txt delete mode 100644 apps/emqx_auth_jwt/etc/emqx_auth_jwt.conf delete mode 100644 apps/emqx_auth_jwt/priv/emqx_auth_jwt.schema delete mode 100644 apps/emqx_auth_jwt/rebar.config delete mode 100644 apps/emqx_auth_jwt/src/emqx_auth_jwt.app.src delete mode 100644 apps/emqx_auth_jwt/src/emqx_auth_jwt.appup.src delete mode 100644 apps/emqx_auth_jwt/src/emqx_auth_jwt.erl delete mode 100644 apps/emqx_auth_jwt/src/emqx_auth_jwt_app.erl delete mode 100644 apps/emqx_auth_jwt/src/emqx_auth_jwt_svr.erl delete mode 100644 apps/emqx_auth_jwt/test/emqx_auth_jwt_SUITE.erl delete mode 100644 apps/emqx_auth_ldap/.gitignore delete mode 100644 apps/emqx_auth_ldap/README.md delete mode 100644 apps/emqx_auth_ldap/emqx.io.ldif delete mode 100644 apps/emqx_auth_ldap/emqx.schema delete mode 100644 apps/emqx_auth_ldap/etc/emqx_auth_ldap.conf delete mode 100644 apps/emqx_auth_ldap/include/emqx_auth_ldap.hrl delete mode 100644 apps/emqx_auth_ldap/priv/emqx_auth_ldap.schema delete mode 100644 apps/emqx_auth_ldap/rebar.config delete mode 100644 apps/emqx_auth_ldap/src/emqx_acl_ldap.erl delete mode 100644 apps/emqx_auth_ldap/src/emqx_auth_ldap.app.src delete mode 100644 apps/emqx_auth_ldap/src/emqx_auth_ldap.erl delete mode 100644 apps/emqx_auth_ldap/src/emqx_auth_ldap_app.erl delete mode 100644 apps/emqx_auth_ldap/src/emqx_auth_ldap_cli.erl delete mode 100644 apps/emqx_auth_ldap/src/emqx_auth_ldap_sup.erl delete mode 100644 apps/emqx_auth_ldap/test/certs/cacert.pem delete mode 100644 apps/emqx_auth_ldap/test/certs/cert.pem delete mode 100644 apps/emqx_auth_ldap/test/certs/client-cert.pem delete mode 100644 apps/emqx_auth_ldap/test/certs/client-key.pem delete mode 100644 apps/emqx_auth_ldap/test/certs/key.pem delete mode 100644 apps/emqx_auth_ldap/test/emqx_auth_ldap_SUITE.erl delete mode 100644 apps/emqx_auth_ldap/test/emqx_auth_ldap_bind_as_user_SUITE.erl delete mode 100644 apps/emqx_auth_mnesia/.gitignore delete mode 100644 apps/emqx_auth_mnesia/README.md delete mode 100644 apps/emqx_auth_mnesia/etc/emqx_auth_mnesia.conf delete mode 100644 apps/emqx_auth_mnesia/include/emqx_auth_mnesia.hrl delete mode 100644 apps/emqx_auth_mnesia/priv/emqx_auth_mnesia.schema delete mode 100644 apps/emqx_auth_mnesia/rebar.config delete mode 100644 apps/emqx_auth_mnesia/src/emqx_acl_mnesia.erl delete mode 100644 apps/emqx_auth_mnesia/src/emqx_acl_mnesia_api.erl delete mode 100644 apps/emqx_auth_mnesia/src/emqx_acl_mnesia_cli.erl delete mode 100644 apps/emqx_auth_mnesia/src/emqx_auth_mnesia.app.src delete mode 100644 apps/emqx_auth_mnesia/src/emqx_auth_mnesia.appup.src delete mode 100644 apps/emqx_auth_mnesia/src/emqx_auth_mnesia.erl delete mode 100644 apps/emqx_auth_mnesia/src/emqx_auth_mnesia_api.erl delete mode 100644 apps/emqx_auth_mnesia/src/emqx_auth_mnesia_app.erl delete mode 100644 apps/emqx_auth_mnesia/src/emqx_auth_mnesia_cli.erl delete mode 100644 apps/emqx_auth_mnesia/src/emqx_auth_mnesia_sup.erl delete mode 100644 apps/emqx_auth_mnesia/test/emqx_acl_mnesia_SUITE.erl delete mode 100644 apps/emqx_auth_mnesia/test/emqx_auth_mnesia_SUITE.erl delete mode 100644 apps/emqx_auth_mongo/.gitignore delete mode 100644 apps/emqx_auth_mongo/CHANGES delete mode 100644 apps/emqx_auth_mongo/README.md delete mode 100644 apps/emqx_auth_mongo/etc/emqx_auth_mongo.conf delete mode 100644 apps/emqx_auth_mongo/include/emqx_auth_mongo.hrl delete mode 100644 apps/emqx_auth_mongo/priv/emqx_auth_mongo.schema delete mode 100644 apps/emqx_auth_mongo/rebar.config delete mode 100644 apps/emqx_auth_mongo/src/emqx_acl_mongo.erl delete mode 100644 apps/emqx_auth_mongo/src/emqx_auth_mongo.app.src delete mode 100644 apps/emqx_auth_mongo/src/emqx_auth_mongo.erl delete mode 100644 apps/emqx_auth_mongo/src/emqx_auth_mongo_app.erl delete mode 100644 apps/emqx_auth_mongo/src/emqx_auth_mongo_sup.erl delete mode 100644 apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE.erl delete mode 100644 apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/ca-key.pem delete mode 100644 apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/ca.pem delete mode 100644 apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/client-cert.pem delete mode 100644 apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/client-key.pem delete mode 100644 apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/mongodb.pem delete mode 100644 apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/private_key.pem delete mode 100644 apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/public_key.pem delete mode 100644 apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/server-cert.pem delete mode 100644 apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/server-key.pem delete mode 100644 apps/emqx_auth_mysql/.gitignore delete mode 100644 apps/emqx_auth_mysql/README.md delete mode 100644 apps/emqx_auth_mysql/etc/emqx_auth_mysql.conf delete mode 100644 apps/emqx_auth_mysql/include/emqx_auth_mysql.hrl delete mode 100644 apps/emqx_auth_mysql/mqtt.sql delete mode 100644 apps/emqx_auth_mysql/priv/emqx_auth_mysql.schema delete mode 100644 apps/emqx_auth_mysql/rebar.config delete mode 100644 apps/emqx_auth_mysql/src/emqx_acl_mysql.erl delete mode 100644 apps/emqx_auth_mysql/src/emqx_auth_mysql.app.src delete mode 100644 apps/emqx_auth_mysql/src/emqx_auth_mysql.erl delete mode 100644 apps/emqx_auth_mysql/src/emqx_auth_mysql_app.erl delete mode 100644 apps/emqx_auth_mysql/src/emqx_auth_mysql_cli.erl delete mode 100644 apps/emqx_auth_mysql/src/emqx_auth_mysql_sup.erl delete mode 100644 apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE.erl delete mode 100644 apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/ca-key.pem delete mode 100644 apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/ca.pem delete mode 100644 apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/client-cert.pem delete mode 100644 apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/client-key.pem delete mode 100644 apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/private_key.pem delete mode 100644 apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/public_key.pem delete mode 100644 apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/server-cert.pem delete mode 100644 apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/server-key.pem delete mode 100644 apps/emqx_auth_pgsql/.gitignore delete mode 100644 apps/emqx_auth_pgsql/README.md delete mode 100644 apps/emqx_auth_pgsql/etc/emqx_auth_pgsql.conf delete mode 100644 apps/emqx_auth_pgsql/include/emqx_auth_pgsql.hrl delete mode 100644 apps/emqx_auth_pgsql/mqtt.sql delete mode 100644 apps/emqx_auth_pgsql/priv/emqx_auth_pgsql.schema delete mode 100644 apps/emqx_auth_pgsql/rebar.config delete mode 100644 apps/emqx_auth_pgsql/src/emqx_acl_pgsql.erl delete mode 100644 apps/emqx_auth_pgsql/src/emqx_auth_pgsql.app.src delete mode 100644 apps/emqx_auth_pgsql/src/emqx_auth_pgsql.erl delete mode 100644 apps/emqx_auth_pgsql/src/emqx_auth_pgsql_app.erl delete mode 100644 apps/emqx_auth_pgsql/src/emqx_auth_pgsql_cli.erl delete mode 100644 apps/emqx_auth_pgsql/src/emqx_auth_pgsql_sup.erl delete mode 100644 apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE.erl delete mode 100644 apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data/ca-key.pem delete mode 100644 apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data/ca.pem delete mode 100644 apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data/client-cert.pem delete mode 100644 apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data/client-key.pem delete mode 100644 apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data/private_key.pem delete mode 100644 apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data/public_key.pem delete mode 100644 apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data/server-cert.pem delete mode 100644 apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data/server-key.pem delete mode 100644 apps/emqx_auth_redis/.gitignore delete mode 100644 apps/emqx_auth_redis/README.md delete mode 100644 apps/emqx_auth_redis/etc/emqx_auth_redis.conf delete mode 100644 apps/emqx_auth_redis/include/emqx_auth_redis.hrl delete mode 100644 apps/emqx_auth_redis/priv/emqx_auth_redis.schema delete mode 100644 apps/emqx_auth_redis/rebar.config delete mode 100644 apps/emqx_auth_redis/src/emqx_acl_redis.erl delete mode 100644 apps/emqx_auth_redis/src/emqx_auth_redis.app.src delete mode 100644 apps/emqx_auth_redis/src/emqx_auth_redis.erl delete mode 100644 apps/emqx_auth_redis/src/emqx_auth_redis_app.erl delete mode 100644 apps/emqx_auth_redis/src/emqx_auth_redis_cli.erl delete mode 100644 apps/emqx_auth_redis/src/emqx_auth_redis_sup.erl delete mode 100644 apps/emqx_auth_redis/test/emqx_auth_redis_SUITE.erl delete mode 100644 apps/emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/ca.crt delete mode 100644 apps/emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/ca.key delete mode 100644 apps/emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/ca.txt delete mode 100644 apps/emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/redis.crt delete mode 100644 apps/emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/redis.dh delete mode 100644 apps/emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/redis.key diff --git a/apps/emqx_auth_http/.gitignore b/apps/emqx_auth_http/.gitignore deleted file mode 100644 index 557a3a337..000000000 --- a/apps/emqx_auth_http/.gitignore +++ /dev/null @@ -1,25 +0,0 @@ -.eunit -deps -*.o -*.beam -*.plt -erl_crash.dump -ebin -rel/example_project -.concrete/DEV_MODE -.rebar -.erlang.mk/ -emqx_auth_http.d -data -ct.cover.spec -cover/ -ct.coverdata -eunit.coverdata -logs/ -erlang.mk -_build/ -rebar.lock -rebar3.crashdump -etc/emqx_auth_http.conf.rendered -.rebar3/ -*.swp diff --git a/apps/emqx_auth_http/README.md b/apps/emqx_auth_http/README.md deleted file mode 100644 index ed743334a..000000000 --- a/apps/emqx_auth_http/README.md +++ /dev/null @@ -1,100 +0,0 @@ -emqx_auth_http -============== - -EMQ X HTTP Auth/ACL Plugin - -Build ------ - -``` -make && make tests -``` - -Configure the Plugin --------------------- - -File: etc/emqx_auth_http.conf - -``` -##-------------------------------------------------------------------- -## Authentication request. -## -## Variables: -## - %u: username -## - %c: clientid -## - %a: ipaddress -## - %r: protocol -## - %P: password -## - %C: common name of client TLS cert -## - %d: subject of client TLS cert -## -## Value: URL -auth.http.auth_req = http://127.0.0.1:8080/mqtt/auth -## Value: post | get | put -auth.http.auth_req.method = post -## Value: Params -auth.http.auth_req.params = clientid=%c,username=%u,password=%P - -##-------------------------------------------------------------------- -## Superuser request. -## -## Variables: -## - %u: username -## - %c: clientid -## - %a: ipaddress -## - %r: protocol -## - %P: password -## - %C: common name of client TLS cert -## - %d: subject of client TLS cert -## -## Value: URL -auth.http.super_req = http://127.0.0.1:8080/mqtt/superuser -## Value: post | get | put -auth.http.super_req.method = post -## Value: Params -auth.http.super_req.params = clientid=%c,username=%u - -##-------------------------------------------------------------------- -## ACL request. -## -## Variables: -## - %A: 1 | 2, 1 = sub, 2 = pub -## - %u: username -## - %c: clientid -## - %a: ipaddress -## - %r: protocol -## - %m: mountpoint -## - %t: topic -## -## Value: URL -auth.http.acl_req = http://127.0.0.1:8080/mqtt/acl -## Value: post | get | put -auth.http.acl_req.method = get -## Value: Params -auth.http.acl_req.params = access=%A,username=%u,clientid=%c,ipaddr=%a,topic=%t -``` - -Load the Plugin ---------------- - -``` -./bin/emqx_ctl plugins load emqx_auth_http -``` - -HTTP API --------- - -200 if ok - -4xx if unauthorized - -License -------- - -Apache License Version 2.0 - -Author ------- - -EMQ X Team. - diff --git a/apps/emqx_auth_http/etc/emqx_auth_http.conf b/apps/emqx_auth_http/etc/emqx_auth_http.conf deleted file mode 100644 index 56e2055c0..000000000 --- a/apps/emqx_auth_http/etc/emqx_auth_http.conf +++ /dev/null @@ -1,172 +0,0 @@ -##-------------------------------------------------------------------- -## HTTP Auth/ACL Plugin -##-------------------------------------------------------------------- - -## HTTP URL API path for Auth Request -## -## Value: URL -## -## Examples: http://127.0.0.1:80/mqtt/auth, https://[::1]:80/mqtt/auth -auth.http.auth_req.url = "http://127.0.0.1:80/mqtt/auth" - -## HTTP Request Method for Auth Request -## -## Value: post | get -auth.http.auth_req.method = post - -## HTTP Request Headers for Auth Request, Content-Type header is configured by default. -## The possible values of the Content-Type header: application/x-www-form-urlencoded, application/json -## -## Examples: auth.http.auth_req.headers.accept = */* - -auth.http.auth_req.headers.content_type = "application/x-www-form-urlencoded" - -## Parameters used to construct the request body or query string parameters -## When the request method is GET, these parameters will be converted into query string parameters -## When the request method is POST, the final format is determined by content-type -## -## Available Variables: -## - %u: username -## - %c: clientid -## - %a: ipaddress -## - %r: protocol -## - %P: password -## - %p: sockport of server accepted -## - %C: common name of client TLS cert -## - %d: subject of client TLS cert -## -## Value: =,=,... -auth.http.auth_req.params = "clientid=%c,username=%u,password=%P" - -## HTTP URL API path for SuperUser Request -## -## Value: URL -## -## Examples: http://127.0.0.1:80/mqtt/superuser, https://[::1]:80/mqtt/superuser -auth.http.super_req.url = "http://127.0.0.1:80/mqtt/superuser" - -## HTTP Request Method for SuperUser Request -## -## Value: post | get -auth.http.super_req.method = post - -## HTTP Request Headers for SuperUser Request, Content-Type header is configured by default. -## The possible values of the Content-Type header: application/x-www-form-urlencoded, application/json -## -## Examples: auth.http.super_req.headers.accept = */* -auth.http.super_req.headers.content-type = "application/x-www-form-urlencoded" - -## Parameters used to construct the request body or query string parameters -## When the request method is GET, these parameters will be converted into query string parameters -## When the request method is POST, the final format is determined by content-type -## -## Available Variables: -## - %u: username -## - %c: clientid -## - %a: ipaddress -## - %r: protocol -## - %P: password -## - %p: sockport of server accepted -## - %C: common name of client TLS cert -## - %d: subject of client TLS cert -## -## Value: =,=,... -auth.http.super_req.params = "clientid=%c,username=%u" - -## HTTP URL API path for ACL Request -## Comment out this config to disable ACL checks -## -## Value: URL -## -## Examples: http://127.0.0.1:80/mqtt/acl, https://[::1]:80/mqtt/acl -auth.http.acl_req.url = "http://127.0.0.1:80/mqtt/acl" - -## HTTP Request Method for ACL Request -## -## Value: post | get -auth.http.acl_req.method = post - -## HTTP Request Headers for ACL Request, Content-Type header is configured by default. -## The possible values of the Content-Type header: application/x-www-form-urlencoded, application/json -## -## Examples: auth.http.acl_req.headers.accept = */* -auth.http.acl_req.headers.content-type = "application/x-www-form-urlencoded" - -## Parameters used to construct the request body or query string parameters -## When the request method is GET, these parameters will be converted into query string parameters -## When the request method is POST, the final format is determined by content-type -## -## Available Variables: -## - %A: access (1 - subscribe, 2 - publish) -## - %u: username -## - %c: clientid -## - %a: ipaddress -## - %r: protocol -## - %P: password -## - %p: sockport of server accepted -## - %C: common name of client TLS cert -## - %d: subject of client TLS cert -## - %t: topic -## -## Value: =,=,... -auth.http.acl_req.params = "access=%A,username=%u,clientid=%c,ipaddr=%a,topic=%t,mountpoint=%m" - -## Time-out time for the request. -## -## Value: Duration -## -h: hour, e.g. '2h' for 2 hours -## -m: minute, e.g. '5m' for 5 minutes -## -s: second, e.g. '30s' for 30 seconds -## -## Default: 5s -auth.http.timeout = 5s - -## Connection time-out time, used during the initial request, -## when the client is connecting to the server. -## -## Value: Duration -## -h: hour, e.g. '2h' for 2 hours -## -m: minute, e.g. '5m' for 5 minutes -## -s: second, e.g. '30s' for 30 seconds -## -## Default: 5s -auth.http.connect_timeout = 5s - -## Connection process pool size -## -## Value: Number -auth.http.pool_size = 32 - -##------------------------------------------------------------------------------ -## SSL options - -## Path to the file containing PEM-encoded CA certificates. The CA certificates -## are used during server authentication and when building the client certificate chain. -## -## Value: File -## auth.http.ssl.cacertfile = "{{ platform_etc_dir }}/certs/ca.pem" - -## The path to a file containing the client's certificate. -## -## Value: File -## auth.http.ssl.certfile = "{{ platform_etc_dir }}/certs/client-cert.pem" - -## Path to a file containing the client's private PEM-encoded key. -## -## Value: File -## auth.http.ssl.keyfile = "{{ platform_etc_dir }}/certs/client-key.pem" - -## In mode verify_none the default behavior is to allow all x509-path -## validation errors. -## -## Value: true | false -## auth.http.ssl.verify = false - -## If not specified, the server's names returned in server's certificate is validated against -## what's provided `auth.http.auth_req.url` config's host part. -## Setting to 'disable' will make EMQ X ignore unmatched server names. -## If set with a host name, the server's names returned in server's certificate is validated -## against this value. -## -## Value: String | disable -## auth.http.ssl.server_name_indication = disable diff --git a/apps/emqx_auth_http/include/emqx_auth_http.hrl b/apps/emqx_auth_http/include/emqx_auth_http.hrl deleted file mode 100644 index 9c1216357..000000000 --- a/apps/emqx_auth_http/include/emqx_auth_http.hrl +++ /dev/null @@ -1,23 +0,0 @@ - --define(APP, emqx_auth_http). - --record(auth_metrics, { - success = 'client.auth.success', - failure = 'client.auth.failure', - ignore = 'client.auth.ignore' - }). - --record(acl_metrics, { - allow = 'client.acl.allow', - deny = 'client.acl.deny', - ignore = 'client.acl.ignore' - }). - --define(METRICS(Type), tl(tuple_to_list(#Type{}))). --define(METRICS(Type, K), #Type{}#Type.K). - --define(AUTH_METRICS, ?METRICS(auth_metrics)). --define(AUTH_METRICS(K), ?METRICS(auth_metrics, K)). - --define(ACL_METRICS, ?METRICS(acl_metrics)). --define(ACL_METRICS(K), ?METRICS(acl_metrics, K)). diff --git a/apps/emqx_auth_http/priv/emqx_auth_http.schema b/apps/emqx_auth_http/priv/emqx_auth_http.schema deleted file mode 100644 index b248c7dc7..000000000 --- a/apps/emqx_auth_http/priv/emqx_auth_http.schema +++ /dev/null @@ -1,131 +0,0 @@ -%%-*- mode: erlang -*- -%% emqx_auth_http config mapping -{mapping, "auth.http.auth_req.url", "emqx_auth_http.auth_req", [ - {datatype, string} -]}. - -{mapping, "auth.http.auth_req.method", "emqx_auth_http.auth_req", [ - {default, post}, - {datatype, {enum, [post, get]}} -]}. - -{mapping, "auth.http.auth_req.headers.$field", "emqx_auth_http.auth_req", [ - {datatype, string} -]}. - -{mapping, "auth.http.auth_req.params", "emqx_auth_http.auth_req", [ - {datatype, string} -]}. - -{translation, "emqx_auth_http.auth_req", fun(Conf) -> - case cuttlefish:conf_get("auth.http.auth_req.url", Conf, undefined) of - undefined -> cuttlefish:unset(); - Url -> - Headers = cuttlefish_variable:filter_by_prefix("auth.http.auth_req.headers", Conf), - Params = cuttlefish:conf_get("auth.http.auth_req.params", Conf), - [{url, Url}, - {method, cuttlefish:conf_get("auth.http.auth_req.method", Conf)}, - {headers, [{K, V} || {[_, _, _, _, K], V} <- Headers]}, - {params, [list_to_tuple(string:tokens(S, "=")) || S <- string:tokens(Params, ",")]}] - end -end}. - -{mapping, "auth.http.super_req.url", "emqx_auth_http.super_req", [ - {datatype, string} -]}. - -{mapping, "auth.http.super_req.method", "emqx_auth_http.super_req", [ - {default, post}, - {datatype, {enum, [post, get]}} -]}. - -{mapping, "auth.http.super_req.headers.$field", "emqx_auth_http.super_req", [ - {datatype, string} -]}. - -{mapping, "auth.http.super_req.params", "emqx_auth_http.super_req", [ - {datatype, string} -]}. - -{translation, "emqx_auth_http.super_req", fun(Conf) -> - case cuttlefish:conf_get("auth.http.super_req.url", Conf, undefined) of - undefined -> cuttlefish:unset(); - Url -> - Headers = cuttlefish_variable:filter_by_prefix("auth.http.super_req.headers", Conf), - Params = cuttlefish:conf_get("auth.http.super_req.params", Conf), - [{url, Url}, - {method, cuttlefish:conf_get("auth.http.super_req.method", Conf)}, - {headers, [{K, V} || {[_, _, _, _, K], V} <- Headers]}, - {params, [list_to_tuple(string:tokens(S, "=")) || S <- string:tokens(Params, ",")]}] - end -end}. - -%% @doc URL for ACL checks. Example: http://127.0.0.1:80/mqtt/acl -%% ACL checks are disabled for this plugin if this config is -%% commented out from the config file, or when the overriding -%% environment variable is set to empty string. -{mapping, "auth.http.acl_req.url", "emqx_auth_http.acl_req", [ - {datatype, string} -]}. - -{mapping, "auth.http.acl_req.method", "emqx_auth_http.acl_req", [ - {default, post}, - {datatype, {enum, [post, get]}} -]}. - -{mapping, "auth.http.acl_req.headers.$field", "emqx_auth_http.acl_req", [ - {datatype, string} -]}. - -{mapping, "auth.http.acl_req.params", "emqx_auth_http.acl_req", [ - {datatype, string} -]}. - -{translation, "emqx_auth_http.acl_req", fun(Conf) -> - case cuttlefish:conf_get("auth.http.acl_req.url", Conf, undefined) of - undefined -> cuttlefish:unset(); - Url -> - Headers = cuttlefish_variable:filter_by_prefix("auth.http.acl_req.headers", Conf), - Params = cuttlefish:conf_get("auth.http.acl_req.params", Conf), - [{url, Url}, - {method, cuttlefish:conf_get("auth.http.acl_req.method", Conf)}, - {headers, [{K, V} || {[_, _, _, _, K], V} <- Headers]}, - {params, [list_to_tuple(string:tokens(S, "=")) || S <- string:tokens(Params, ",")]}] - end -end}. - -{mapping, "auth.http.timeout", "emqx_auth_http.timeout", [ - {default, "5s"}, - {datatype, [integer, {duration, ms}]} -]}. - -{mapping, "auth.http.connect_timeout", "emqx_auth_http.connect_timeout", [ - {default, "5s"}, - {datatype, [integer, {duration, ms}]} -]}. - -{mapping, "auth.http.pool_size", "emqx_auth_http.pool_size", [ - {default, 8}, - {datatype, integer} -]}. - -{mapping, "auth.http.ssl.cacertfile", "emqx_auth_http.cacertfile", [ - {datatype, string} -]}. - -{mapping, "auth.http.ssl.certfile", "emqx_auth_http.certfile", [ - {datatype, string} -]}. - -{mapping, "auth.http.ssl.keyfile", "emqx_auth_http.keyfile", [ - {datatype, string} -]}. - -{mapping, "auth.http.ssl.verify", "emqx_auth_http.verify", [ - {default, false}, - {datatype, {enum, [true, false]}} -]}. - -{mapping, "auth.http.ssl.server_name_indication", "emqx_auth_http.server_name_indication", [ - {datatype, string} -]}. diff --git a/apps/emqx_auth_http/rebar.config b/apps/emqx_auth_http/rebar.config deleted file mode 100644 index 01c0f4209..000000000 --- a/apps/emqx_auth_http/rebar.config +++ /dev/null @@ -1,26 +0,0 @@ -{deps, []}. - -{edoc_opts, [{preprocess, true}]}. -{erl_opts, [warn_unused_vars, - warn_shadow_vars, - warn_unused_import, - warn_obsolete_guard, - debug_info, - {parse_transform}]}. - -{xref_checks, [undefined_function_calls, undefined_functions, - locals_not_used, deprecated_function_calls, - warnings_as_errors, deprecated_functions]}. - -{cover_enabled, true}. -{cover_opts, [verbose]}. -{cover_export_enabled, true}. - -{profiles, - [{test, - [{deps, - [ - {emqtt, {git, "https://github.com/emqx/emqtt", {tag, "v1.2.2"}}} - ]} - ]} - ]}. diff --git a/apps/emqx_auth_http/src/emqx_acl_http.erl b/apps/emqx_auth_http/src/emqx_acl_http.erl deleted file mode 100644 index aa98759b0..000000000 --- a/apps/emqx_auth_http/src/emqx_acl_http.erl +++ /dev/null @@ -1,88 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_acl_http). - --include("emqx_auth_http.hrl"). - --include_lib("emqx/include/emqx.hrl"). --include_lib("emqx/include/logger.hrl"). - --logger_header("[ACL http]"). - --import(emqx_auth_http_cli, - [ request/6 - , feedvar/2 - ]). - -%% ACL callbacks --export([ register_metrics/0 - , check_acl/5 - , description/0 - ]). - --spec(register_metrics() -> ok). -register_metrics() -> - lists:foreach(fun emqx_metrics:ensure/1, ?ACL_METRICS). - -%%-------------------------------------------------------------------- -%% ACL callbacks -%%-------------------------------------------------------------------- - -check_acl(ClientInfo, PubSub, Topic, AclResult, Params) -> - return_with(fun inc_metrics/1, - do_check_acl(ClientInfo, PubSub, Topic, AclResult, Params)). - -do_check_acl(#{username := <<$$, _/binary>>}, _PubSub, _Topic, _AclResult, _Params) -> - ok; -do_check_acl(ClientInfo, PubSub, Topic, _AclResult, #{acl := ACLParams = #{path := Path}}) -> - ClientInfo1 = ClientInfo#{access => access(PubSub), topic => Topic}, - case check_acl_request(ACLParams, ClientInfo1) of - {ok, 200, <<"ignore">>} -> ok; - {ok, 200, _Body} -> {stop, allow}; - {ok, _Code, _Body} -> {stop, deny}; - {error, Error} -> - ?LOG(error, "Request ACL path ~s, error: ~p", [Path, Error]), - ok - end. - -description() -> "ACL with HTTP API". - -%%-------------------------------------------------------------------- -%% Internal functions -%%-------------------------------------------------------------------- - -inc_metrics(ok) -> - emqx_metrics:inc(?ACL_METRICS(ignore)); -inc_metrics({stop, allow}) -> - emqx_metrics:inc(?ACL_METRICS(allow)); -inc_metrics({stop, deny}) -> - emqx_metrics:inc(?ACL_METRICS(deny)). - -return_with(Fun, Result) -> - Fun(Result), Result. - -check_acl_request(#{pool_name := PoolName, - path := Path, - method := Method, - headers := Headers, - params := Params, - timeout := Timeout}, ClientInfo) -> - request(PoolName, Method, Path, Headers, feedvar(Params, ClientInfo), Timeout). - -access(subscribe) -> 1; -access(publish) -> 2. - diff --git a/apps/emqx_auth_http/src/emqx_auth_http.app.src b/apps/emqx_auth_http/src/emqx_auth_http.app.src deleted file mode 100644 index b2c3221e6..000000000 --- a/apps/emqx_auth_http/src/emqx_auth_http.app.src +++ /dev/null @@ -1,14 +0,0 @@ -{application, emqx_auth_http, - [{description, "EMQ X Authentication/ACL with HTTP API"}, - {vsn, "4.3.0"}, % strict semver, bump manually! - {modules, []}, - {registered, [emqx_auth_http_sup]}, - {applications, [kernel,stdlib,ehttpc]}, - {mod, {emqx_auth_http_app, []}}, - {env, []}, - {licenses, ["Apache-2.0"]}, - {maintainers, ["EMQ X Team "]}, - {links, [{"Homepage", "https://emqx.io/"}, - {"Github", "https://github.com/emqx/emqx-auth-http"} - ]} - ]}. diff --git a/apps/emqx_auth_http/src/emqx_auth_http.erl b/apps/emqx_auth_http/src/emqx_auth_http.erl deleted file mode 100644 index aba0a5d8d..000000000 --- a/apps/emqx_auth_http/src/emqx_auth_http.erl +++ /dev/null @@ -1,112 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_auth_http). - --include("emqx_auth_http.hrl"). - --include_lib("emqx/include/emqx.hrl"). --include_lib("emqx/include/logger.hrl"). --include_lib("emqx/include/types.hrl"). - --logger_header("[Auth http]"). - --import(emqx_auth_http_cli, - [ request/6 - , feedvar/2 - ]). - -%% Callbacks --export([ register_metrics/0 - , check/3 - , description/0 - ]). - --spec(register_metrics() -> ok). -register_metrics() -> - lists:foreach(fun emqx_metrics:ensure/1, ?AUTH_METRICS). - -check(ClientInfo, AuthResult, #{auth := AuthParms = #{path := Path}, - super := SuperParams}) -> - case authenticate(AuthParms, ClientInfo) of - {ok, 200, <<"ignore">>} -> - emqx_metrics:inc(?AUTH_METRICS(ignore)), ok; - {ok, 200, Body} -> - emqx_metrics:inc(?AUTH_METRICS(success)), - IsSuperuser = is_superuser(SuperParams, ClientInfo), - {stop, AuthResult#{is_superuser => IsSuperuser, - auth_result => success, - anonymous => false, - mountpoint => mountpoint(Body, ClientInfo)}}; - {ok, Code, _Body} -> - ?LOG(error, "Deny connection from path: ~s, response http code: ~p", - [Path, Code]), - emqx_metrics:inc(?AUTH_METRICS(failure)), - {stop, AuthResult#{auth_result => http_to_connack_error(Code), - anonymous => false}}; - {error, Error} -> - ?LOG(error, "Request auth path: ~s, error: ~p", [Path, Error]), - emqx_metrics:inc(?AUTH_METRICS(failure)), - %%FIXME later: server_unavailable is not right. - {stop, AuthResult#{auth_result => server_unavailable, - anonymous => false}} - end. - -description() -> "Authentication by HTTP API". - -%%-------------------------------------------------------------------- -%% Requests -%%-------------------------------------------------------------------- - -authenticate(#{pool_name := PoolName, - path := Path, - method := Method, - headers := Headers, - params := Params, - timeout := Timeout}, ClientInfo) -> - request(PoolName, Method, Path, Headers, feedvar(Params, ClientInfo), Timeout). - --spec(is_superuser(maybe(map()), emqx_types:client()) -> boolean()). -is_superuser(undefined, _ClientInfo) -> - false; -is_superuser(#{pool_name := PoolName, - path := Path, - method := Method, - headers := Headers, - params := Params, - timeout := Timeout}, ClientInfo) -> - case request(PoolName, Method, Path, Headers, feedvar(Params, ClientInfo), Timeout) of - {ok, 200, _Body} -> true; - {ok, _Code, _Body} -> false; - {error, Error} -> ?LOG(error, "Request superuser path ~s, error: ~p", [Path, Error]), - false - end. - -mountpoint(Body, #{mountpoint := Mountpoint}) -> - case emqx_json:safe_decode(Body, [return_maps]) of - {error, _} -> Mountpoint; - {ok, Json} when is_map(Json) -> - maps:get(<<"mountpoint">>, Json, Mountpoint); - {ok, _NotMap} -> Mountpoint - end. - -http_to_connack_error(400) -> bad_username_or_password; -http_to_connack_error(401) -> bad_username_or_password; -http_to_connack_error(403) -> not_authorized; -http_to_connack_error(429) -> banned; -http_to_connack_error(503) -> server_unavailable; -http_to_connack_error(504) -> server_busy; -http_to_connack_error(_) -> server_unavailable. diff --git a/apps/emqx_auth_http/src/emqx_auth_http_app.erl b/apps/emqx_auth_http/src/emqx_auth_http_app.erl deleted file mode 100644 index 7d4781f7c..000000000 --- a/apps/emqx_auth_http/src/emqx_auth_http_app.erl +++ /dev/null @@ -1,158 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_auth_http_app). - --behaviour(application). - --emqx_plugin(auth). - --include("emqx_auth_http.hrl"). - --export([ start/2 - , stop/1 - ]). - -%%-------------------------------------------------------------------- -%% Application Callbacks -%%-------------------------------------------------------------------- - -start(_StartType, _StartArgs) -> - {ok, Sup} = emqx_auth_http_sup:start_link(), - translate_env(), - load_hooks(), - {ok, Sup}. - -stop(_State) -> - unload_hooks(). - -%%-------------------------------------------------------------------- -%% Internel functions -%%-------------------------------------------------------------------- - -translate_env() -> - lists:foreach(fun translate_env/1, [auth_req, super_req, acl_req]). - -translate_env(EnvName) -> - case application:get_env(?APP, EnvName) of - undefined -> ok; - {ok, Req} -> - {ok, PoolSize} = application:get_env(?APP, pool_size), - {ok, ConnectTimeout} = application:get_env(?APP, connect_timeout), - URL = proplists:get_value(url, Req), - {ok, #{host := Host, - path := Path0, - port := Port, - scheme := Scheme}} = emqx_http_lib:uri_parse(URL), - Path = path(Path0), - MoreOpts = case Scheme of - http -> - [{transport_opts, emqx_misc:ipv6_probe([])}]; - https -> - CACertFile = application:get_env(?APP, cacertfile, undefined), - CertFile = application:get_env(?APP, certfile, undefined), - KeyFile = application:get_env(?APP, keyfile, undefined), - Verify = case application:get_env(?APP, verify, fasle) of - true -> verify_peer; - false -> verify_none - end, - SNI = case application:get_env(?APP, server_name_indication, undefined) of - "disable" -> disable; - SNI0 -> SNI0 - end, - TLSOpts = lists:filter( - fun({_, V}) -> - V =/= <<>> andalso V =/= undefined - end, [{keyfile, KeyFile}, - {certfile, CertFile}, - {cacertfile, CACertFile}, - {verify, Verify}, - {server_name_indication, SNI}]), - NTLSOpts = [ {versions, emqx_tls_lib:default_versions()} - , {ciphers, emqx_tls_lib:default_ciphers()} - | TLSOpts - ], - [{transport, ssl}, {transport_opts, emqx_misc:ipv6_probe(NTLSOpts)}] - end, - PoolOpts = [{host, Host}, - {port, Port}, - {pool_size, PoolSize}, - {pool_type, random}, - {connect_timeout, ConnectTimeout}, - {retry, 5}, - {retry_timeout, 1000}] ++ MoreOpts, - Method = proplists:get_value(method, Req), - Headers = proplists:get_value(headers, Req), - NHeaders = ensure_content_type_header(Method, emqx_http_lib:normalise_headers(Headers)), - NReq = lists:keydelete(headers, 1, Req), - {ok, Timeout} = application:get_env(?APP, timeout), - application:set_env(?APP, EnvName, [{path, Path}, - {headers, NHeaders}, - {timeout, Timeout}, - {pool_name, list_to_atom("emqx_auth_http/" ++ atom_to_list(EnvName))}, - {pool_opts, PoolOpts} | NReq]) - end. - -load_hooks() -> - case application:get_env(?APP, auth_req) of - undefined -> ok; - {ok, AuthReq} -> - ok = emqx_auth_http:register_metrics(), - PoolOpts = proplists:get_value(pool_opts, AuthReq), - PoolName = proplists:get_value(pool_name, AuthReq), - {ok, _} = ehttpc_sup:start_pool(PoolName, PoolOpts), - case application:get_env(?APP, super_req) of - undefined -> - emqx_hooks:put('client.authenticate', {emqx_auth_http, check, [#{auth => maps:from_list(AuthReq), - super => undefined}]}); - {ok, SuperReq} -> - PoolOpts1 = proplists:get_value(pool_opts, SuperReq), - PoolName1 = proplists:get_value(pool_name, SuperReq), - {ok, _} = ehttpc_sup:start_pool(PoolName1, PoolOpts1), - emqx_hooks:put('client.authenticate', {emqx_auth_http, check, [#{auth => maps:from_list(AuthReq), - super => maps:from_list(SuperReq)}]}) - end - end, - case application:get_env(?APP, acl_req) of - undefined -> ok; - {ok, ACLReq} -> - ok = emqx_acl_http:register_metrics(), - PoolOpts2 = proplists:get_value(pool_opts, ACLReq), - PoolName2 = proplists:get_value(pool_name, ACLReq), - {ok, _} = ehttpc_sup:start_pool(PoolName2, PoolOpts2), - emqx_hooks:put('client.check_acl', {emqx_acl_http, check_acl, [#{acl => maps:from_list(ACLReq)}]}) - end, - ok. - -unload_hooks() -> - emqx:unhook('client.authenticate', {emqx_auth_http, check}), - emqx:unhook('client.check_acl', {emqx_acl_http, check_acl}), - _ = ehttpc_sup:stop_pool('emqx_auth_http/auth_req'), - _ = ehttpc_sup:stop_pool('emqx_auth_http/super_req'), - _ = ehttpc_sup:stop_pool('emqx_auth_http/acl_req'), - ok. - -ensure_content_type_header(Method, Headers) - when Method =:= post orelse Method =:= put -> - Headers; -ensure_content_type_header(_Method, Headers) -> - lists:keydelete("content-type", 1, Headers). - -path("") -> - "/"; -path(Path) -> - Path. - diff --git a/apps/emqx_auth_http/src/emqx_auth_http_cli.erl b/apps/emqx_auth_http/src/emqx_auth_http_cli.erl deleted file mode 100644 index 979ac475d..000000000 --- a/apps/emqx_auth_http/src/emqx_auth_http_cli.erl +++ /dev/null @@ -1,92 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_auth_http_cli). - --include("emqx_auth_http.hrl"). - --export([ request/6 - , feedvar/2 - , feedvar/3 - ]). - -%%-------------------------------------------------------------------- -%% HTTP Request -%%-------------------------------------------------------------------- - -request(PoolName, get, Path, Headers, Params, Timeout) -> - NewPath = Path ++ "?" ++ binary_to_list(cow_qs:qs(bin_kw(Params))), - reply(ehttpc:request(ehttpc_pool:pick_worker(PoolName), get, {NewPath, Headers}, Timeout)); - -request(PoolName, post, Path, Headers, Params, Timeout) -> - Body = case proplists:get_value("content-type", Headers) of - "application/x-www-form-urlencoded" -> - cow_qs:qs(bin_kw(Params)); - "application/json" -> - emqx_json:encode(bin_kw(Params)) - end, - reply(ehttpc:request(ehttpc_pool:pick_worker(PoolName), post, {Path, Headers, Body}, Timeout)). - -reply({ok, StatusCode, _Headers}) -> - {ok, StatusCode, <<>>}; -reply({ok, StatusCode, _Headers, Body}) -> - {ok, StatusCode, Body}; -reply({error, Reason}) -> - {error, Reason}. - -%% TODO: move this conversion to cuttlefish config and schema -bin_kw(KeywordList) when is_list(KeywordList) -> - [{bin(K), bin(V)} || {K, V} <- KeywordList]. - -bin(Atom) when is_atom(Atom) -> - list_to_binary(atom_to_list(Atom)); -bin(Int) when is_integer(Int) -> - integer_to_binary(Int); -bin(Float) when is_float(Float) -> - float_to_binary(Float, [{decimals, 12}, compact]); -bin(List) when is_list(List)-> - list_to_binary(List); -bin(Binary) when is_binary(Binary) -> - Binary. - -%%-------------------------------------------------------------------- -%% Feed Variables -%%-------------------------------------------------------------------- - -feedvar(Params, ClientInfo = #{clientid := ClientId, - protocol := Protocol, - peerhost := Peerhost}) -> - lists:map(fun({Param, "%u"}) -> {Param, maps:get(username, ClientInfo, null)}; - ({Param, "%c"}) -> {Param, ClientId}; - ({Param, "%r"}) -> {Param, Protocol}; - ({Param, "%a"}) -> {Param, inet:ntoa(Peerhost)}; - ({Param, "%P"}) -> {Param, maps:get(password, ClientInfo, null)}; - ({Param, "%p"}) -> {Param, maps:get(sockport, ClientInfo, null)}; - ({Param, "%C"}) -> {Param, maps:get(cn, ClientInfo, null)}; - ({Param, "%d"}) -> {Param, maps:get(dn, ClientInfo, null)}; - ({Param, "%A"}) -> {Param, maps:get(access, ClientInfo, null)}; - ({Param, "%t"}) -> {Param, maps:get(topic, ClientInfo, null)}; - ({Param, "%m"}) -> {Param, maps:get(mountpoint, ClientInfo, null)}; - ({Param, Var}) -> {Param, Var} - end, Params). - -feedvar(Params, Var, Val) -> - lists:map(fun({Param, Var0}) when Var0 == Var -> - {Param, Val}; - ({Param, Var0}) -> - {Param, Var0} - end, Params). - diff --git a/apps/emqx_auth_http/src/emqx_auth_http_sup.erl b/apps/emqx_auth_http/src/emqx_auth_http_sup.erl deleted file mode 100644 index 798a8a92e..000000000 --- a/apps/emqx_auth_http/src/emqx_auth_http_sup.erl +++ /dev/null @@ -1,29 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_auth_http_sup). - --behaviour(supervisor). - --export([start_link/0]). - --export([init/1]). - -start_link() -> - supervisor:start_link({local, ?MODULE}, ?MODULE, []). - -init([]) -> - {ok, {{one_for_all, 0, 1}, []}}. diff --git a/apps/emqx_auth_http/test/emqx_auth_http_SUITE.erl b/apps/emqx_auth_http/test/emqx_auth_http_SUITE.erl deleted file mode 100644 index ef692e886..000000000 --- a/apps/emqx_auth_http/test/emqx_auth_http_SUITE.erl +++ /dev/null @@ -1,257 +0,0 @@ -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. - --module(emqx_auth_http_SUITE). - --compile(export_all). --compile(nowarn_export_all). - --include_lib("emqx/include/emqx.hrl"). --include_lib("common_test/include/ct.hrl"). --include_lib("eunit/include/eunit.hrl"). - --define(APP, emqx_auth_http). - --define(USER(ClientId, Username, Protocol, Peerhost, Zone), - #{clientid => ClientId, username => Username, protocol => Protocol, - peerhost => Peerhost, zone => Zone}). - --define(USER(ClientId, Username, Protocol, Peerhost, Zone, Mountpoint), - #{clientid => ClientId, username => Username, protocol => Protocol, - peerhost => Peerhost, zone => Zone, mountpoint => Mountpoint}). - -%%-------------------------------------------------------------------- -%% Setups -%%-------------------------------------------------------------------- - -all() -> - [ - {group, http_inet}, - {group, http_inet6}, - {group, https_inet}, - {group, https_inet6}, - pub_sub_no_acl, - no_hook_if_config_unset - ]. - -groups() -> - Cases = emqx_ct:all(?MODULE), - [{Name, Cases} || Name <- [http_inet, http_inet6, https_inet, https_inet6]]. - -init_per_group(GrpName, Cfg) -> - [Scheme, Inet] = [list_to_atom(X) || X <- string:tokens(atom_to_list(GrpName), "_")], - ok = setup(Scheme, Inet), - Cfg. - -end_per_group(_GrpName, _Cfg) -> - teardown(). - -init_per_testcase(pub_sub_no_acl, Cfg) -> - Scheme = http, - Inet = inet, - http_auth_server:start(Scheme, Inet), - Fun = fun(App) -> set_special_configs(App, Scheme, Inet, no_acl) end, - emqx_ct_helpers:start_apps([emqx_auth_http], Fun), - ?assert(is_hooked('client.authenticate')), - ?assertNot(is_hooked('client.check_acl')), - Cfg; -init_per_testcase(no_hook_if_config_unset, Cfg) -> - setup(http, inet), - Cfg; -init_per_testcase(_, Cfg) -> - %% init per group - Cfg. - -end_per_testcase(pub_sub_no_acl, _Cfg) -> - teardown(); -end_per_testcase(no_hook_if_config_unset, _Cfg) -> - teardown(); -end_per_testcase(_, _Cfg) -> - %% teardown per group - ok. - -setup(Scheme, Inet) -> - http_auth_server:start(Scheme, Inet), - Fun = fun(App) -> set_special_configs(App, Scheme, Inet, normal) end, - emqx_ct_helpers:start_apps([emqx_auth_http], Fun), - ?assert(is_hooked('client.authenticate')), - ?assert(is_hooked('client.check_acl')). - -teardown() -> - http_auth_server:stop(), - application:stop(emqx_auth_http), - ?assertNot(is_hooked('client.authenticate')), - ?assertNot(is_hooked('client.check_acl')), - emqx_ct_helpers:stop_apps([emqx]). - -set_special_configs(emqx, _Scheme, _Inet, _AuthConfig) -> - application:set_env(emqx, allow_anonymous, true), - application:set_env(emqx, enable_acl_cache, false), - LoadedPluginPath = filename:join(["test", "emqx_SUITE_data", "loaded_plugins"]), - application:set_env(emqx, plugins_loaded_file, - emqx_ct_helpers:deps_path(emqx, LoadedPluginPath)); - -set_special_configs(emqx_auth_http, Scheme, Inet, PluginConfig) -> - [application:unset_env(?APP, Par) || Par <- [acl_req, auth_req]], - ServerAddr = http_server(Scheme, Inet), - - AuthReq = #{method => get, - url => ServerAddr ++ "/mqtt/auth", - headers => [{"content-type", "application/json"}], - params => [{"clientid", "%c"}, {"username", "%u"}, {"password", "%P"}]}, - SuperReq = #{method => post, - url => ServerAddr ++ "/mqtt/superuser", - headers => [{"content-type", "application/json"}], - params => [{"clientid", "%c"}, {"username", "%u"}]}, - AclReq = #{method => post, - url => ServerAddr ++ "/mqtt/acl", - headers => [{"content-type", "application/json"}], - params => [{"access", "%A"}, {"username", "%u"}, {"clientid", "%c"}, {"ipaddr", "%a"}, {"topic", "%t"}, {"mountpoint", "%m"}]}, - - Scheme =:= https andalso set_https_client_opts(), - - application:set_env(emqx_auth_http, auth_req, maps:to_list(AuthReq)), - application:set_env(emqx_auth_http, super_req, maps:to_list(SuperReq)), - case PluginConfig of - normal -> ok = application:set_env(emqx_auth_http, acl_req, maps:to_list(AclReq)); - no_acl -> ok - end. - -%% @private -set_https_client_opts() -> - SSLOpt = emqx_ct_helpers:client_ssl_twoway(), - application:set_env(emqx_auth_http, cacertfile, proplists:get_value(cacertfile, SSLOpt, undefined)), - application:set_env(emqx_auth_http, certfile, proplists:get_value(certfile, SSLOpt, undefined)), - application:set_env(emqx_auth_http, keyfile, proplists:get_value(keyfile, SSLOpt, undefined)), - application:set_env(emqx_auth_http, verify, true), - application:set_env(emqx_auth_http, server_name_indication, "disable"). - -%% @private -http_server(http, inet) -> "http://127.0.0.1:8991"; % ipv4 -http_server(http, inet6) -> "http://localhost:8991"; % test hostname resolution -http_server(https, inet) -> "https://localhost:8991"; % test hostname resolution -http_server(https, inet6) -> "https://[::1]:8991". % ipv6 - -%%------------------------------------------------------------------------------ -%% Testcases -%%------------------------------------------------------------------------------ - -t_check_acl(Cfg) when is_list(Cfg) -> - SuperUser = ?USER(<<"superclient">>, <<"superuser">>, mqtt, {127,0,0,1}, external), - deny = emqx_access_control:check_acl(SuperUser, subscribe, <<"users/testuser/1">>), - deny = emqx_access_control:check_acl(SuperUser, publish, <<"anytopic">>), - - User1 = ?USER(<<"client1">>, <<"testuser">>, mqtt, {127,0,0,1}, external), - UnIpUser1 = ?USER(<<"client1">>, <<"testuser">>, mqtt, {192,168,0,4}, external), - UnClientIdUser1 = ?USER(<<"unkonwc">>, <<"testuser">>, mqtt, {127,0,0,1}, external), - UnnameUser1= ?USER(<<"client1">>, <<"unuser">>, mqtt, {127,0,0,1}, external), - allow = emqx_access_control:check_acl(User1, subscribe, <<"users/testuser/1">>), - deny = emqx_access_control:check_acl(User1, publish, <<"users/testuser/1">>), - deny = emqx_access_control:check_acl(UnIpUser1, subscribe, <<"users/testuser/1">>), - deny = emqx_access_control:check_acl(UnClientIdUser1, subscribe, <<"users/testuser/1">>), - deny = emqx_access_control:check_acl(UnnameUser1, subscribe, <<"$SYS/testuser/1">>), - - User2 = ?USER(<<"client2">>, <<"xyz">>, mqtt, {127,0,0,1}, external), - UserC = ?USER(<<"client2">>, <<"xyz">>, mqtt, {192,168,1,3}, external), - allow = emqx_access_control:check_acl(UserC, publish, <<"a/b/c">>), - deny = emqx_access_control:check_acl(User2, publish, <<"a/b/c">>), - deny = emqx_access_control:check_acl(User2, subscribe, <<"$SYS/testuser/1">>). - -t_check_auth(Cfg) when is_list(Cfg) -> - User1 = ?USER(<<"client1">>, <<"testuser1">>, mqtt, {127,0,0,1}, external, undefined), - User2 = ?USER(<<"client2">>, <<"testuser2">>, mqtt, {127,0,0,1}, exteneral, undefined), - User3 = ?USER(<<"client3">>, undefined, mqtt, {127,0,0,1}, exteneral, undefined), - - {ok, #{auth_result := success, - anonymous := false, - is_superuser := false}} = emqx_access_control:authenticate(User1#{password => <<"pass1">>}), - {error, bad_username_or_password} = emqx_access_control:authenticate(User1#{password => <<"pass">>}), - {error, bad_username_or_password} = emqx_access_control:authenticate(User1#{password => <<>>}), - - {ok, #{is_superuser := false}} = emqx_access_control:authenticate(User2#{password => <<"pass2">>}), - {error, bad_username_or_password} = emqx_access_control:authenticate(User2#{password => <<>>}), - {error, bad_username_or_password} = emqx_access_control:authenticate(User2#{password => <<"errorpwd">>}), - - {error, bad_username_or_password} = emqx_access_control:authenticate(User3#{password => <<"pwd">>}). - -pub_sub_no_acl(Cfg) when is_list(Cfg) -> - {ok, T1} = emqtt:start_link([{host, "localhost"}, - {clientid, <<"client1">>}, - {username, <<"testuser1">>}, - {password, <<"pass1">>}]), - {ok, _} = emqtt:connect(T1), - emqtt:publish(T1, <<"topic">>, <<"body">>, [{qos, 0}, {retain, true}]), - timer:sleep(1000), - {ok, T2} = emqtt:start_link([{host, "localhost"}, - {clientid, <<"client2">>}, - {username, <<"testuser2">>}, - {password, <<"pass2">>}]), - {ok, _} = emqtt:connect(T2), - emqtt:subscribe(T2, <<"topic">>), - receive - {publish, _Topic, Payload} -> - ?assertEqual(<<"body">>, Payload) - after 1000 -> false end, - emqtt:disconnect(T1), - emqtt:disconnect(T2). - -t_pub_sub(Cfg) when is_list(Cfg) -> - {ok, T1} = emqtt:start_link([{host, "localhost"}, - {clientid, <<"client1">>}, - {username, <<"testuser1">>}, - {password, <<"pass1">>}]), - {ok, _} = emqtt:connect(T1), - emqtt:publish(T1, <<"topic">>, <<"body">>, [{qos, 0}, {retain, true}]), - timer:sleep(1000), - {ok, T2} = emqtt:start_link([{host, "localhost"}, - {clientid, <<"client2">>}, - {username, <<"testuser2">>}, - {password, <<"pass2">>}]), - {ok, _} = emqtt:connect(T2), - emqtt:subscribe(T2, <<"topic">>), - receive - {publish, _Topic, Payload} -> - ?assertEqual(<<"body">>, Payload) - after 1000 -> false end, - emqtt:disconnect(T1), - emqtt:disconnect(T2). - -no_hook_if_config_unset(Cfg) when is_list(Cfg) -> - ?assert(is_hooked('client.authenticate')), - ?assert(is_hooked('client.check_acl')), - application:stop(?APP), - [application:unset_env(?APP, Par) || Par <- [acl_req, auth_req]], - application:start(?APP), - ?assertEqual([], emqx_hooks:lookup('client.authenticate')), - ?assertNot(is_hooked('client.authenticate')), - ?assertNot(is_hooked('client.check_acl')). - -is_hooked(HookName) -> - Callbacks = emqx_hooks:lookup(HookName), - F = fun(Callback) -> - case emqx_hooks:callback_action(Callback) of - {emqx_auth_http, check, _} -> - 'client.authenticate' = HookName, % assert - true; - {emqx_acl_http, check_acl, _} -> - 'client.check_acl' = HookName, % assert - true; - _ -> - false - end - end, - case lists:filter(F, Callbacks) of - [_] -> true; - [] -> false - end. diff --git a/apps/emqx_auth_http/test/http_auth_server.erl b/apps/emqx_auth_http/test/http_auth_server.erl deleted file mode 100644 index 54c4d38b3..000000000 --- a/apps/emqx_auth_http/test/http_auth_server.erl +++ /dev/null @@ -1,152 +0,0 @@ --module(http_auth_server). - --export([ start/2 - , stop/0 - ]). - --define(SUPERUSER, [[{"username", "superuser"}, {"clientid", "superclient"}]]). - --define(ACL, [[{<<"username">>, <<"testuser">>}, - {<<"clientid">>, <<"client1">>}, - {<<"access">>, <<"1">>}, - {<<"topic">>, <<"users/testuser/1">>}, - {<<"ipaddr">>, <<"127.0.0.1">>}, - {<<"mountpoint">>, <<"null">>}], - [{<<"username">>, <<"xyz">>}, - {<<"clientid">>, <<"client2">>}, - {<<"access">>, <<"2">>}, - {<<"topic">>, <<"a/b/c">>}, - {<<"ipaddr">>, <<"192.168.1.3">>}, - {<<"mountpoint">>, <<"null">>}], - [{<<"username">>, <<"testuser1">>}, - {<<"clientid">>, <<"client1">>}, - {<<"access">>, <<"2">>}, - {<<"topic">>, <<"topic">>}, - {<<"ipaddr">>, <<"127.0.0.1">>}, - {<<"mountpoint">>, <<"null">>}], - [{<<"username">>, <<"testuser2">>}, - {<<"clientid">>, <<"client2">>}, - {<<"access">>, <<"1">>}, - {<<"topic">>, <<"topic">>}, - {<<"ipaddr">>, <<"127.0.0.1">>}, - {<<"mountpoint">>, <<"null">>}]]). - --define(AUTH, [[{<<"clientid">>, <<"client1">>}, - {<<"username">>, <<"testuser1">>}, - {<<"password">>, <<"pass1">>}], - [{<<"clientid">>, <<"client2">>}, - {<<"username">>, <<"testuser2">>}, - {<<"password">>, <<"pass2">>}]]). - -%%------------------------------------------------------------------------------ -%% REST Interface -%%------------------------------------------------------------------------------ - --rest_api(#{ name => auth - , method => 'GET' - , path => "/mqtt/auth" - , func => authenticate - , descr => "Authenticate user access permission" - }). - --rest_api(#{ name => is_superuser - , method => 'GET' - , path => "/mqtt/superuser" - , func => is_superuser - , descr => "Is super user" - }). - --rest_api(#{ name => acl - , method => 'GET' - , path => "/mqtt/acl" - , func => check_acl - , descr => "Check acl" - }). - --rest_api(#{ name => auth - , method => 'POST' - , path => "/mqtt/auth" - , func => authenticate - , descr => "Authenticate user access permission" - }). - --rest_api(#{ name => is_superuser - , method => 'POST' - , path => "/mqtt/superuser" - , func => is_superuser - , descr => "Is super user" - }). - --rest_api(#{ name => acl - , method => 'POST' - , path => "/mqtt/acl" - , func => check_acl - , descr => "Check acl" - }). - --export([ authenticate/2 - , is_superuser/2 - , check_acl/2 - ]). - -authenticate(_Binding, Params) -> - return(check(Params, ?AUTH)). - -is_superuser(_Binding, Params) -> - return(check(Params, ?SUPERUSER)). - -check_acl(_Binding, Params) -> - return(check(Params, ?ACL)). - -return(allow) -> {200, <<"allow">>}; -return(deny) -> {400, <<"deny">>}. - -start(http, Inet) -> - application:ensure_all_started(minirest), - Handlers = [{"/", minirest:handler(#{modules => [?MODULE]})}], - Dispatch = [{"/[...]", minirest, Handlers}], - minirest:start_http(http_auth_server, #{socket_opts => [Inet, {port, 8991}]}, Dispatch); - -start(https, Inet) -> - application:ensure_all_started(minirest), - Handlers = [{"/", minirest:handler(#{modules => [?MODULE]})}], - Dispatch = [{"/[...]", minirest, Handlers}], - minirest:start_https(http_auth_server, #{socket_opts => [Inet, {port, 8991} | certopts()]}, Dispatch). - -%% @private -certopts() -> - Certfile = filename:join(["etc", "certs", "cert.pem"]), - Keyfile = filename:join(["etc", "certs", "key.pem"]), - CaCert = filename:join(["etc", "certs", "cacert.pem"]), - [{verify, verify_peer}, - {certfile, emqx_ct_helpers:deps_path(emqx, Certfile)}, - {keyfile, emqx_ct_helpers:deps_path(emqx, Keyfile)}, - {cacertfile, emqx_ct_helpers:deps_path(emqx, CaCert)}] ++ emqx_ct_helpers:client_ssl(). - -stop() -> - minirest:stop_http(http_auth_server). - --spec check(HttpReqParams :: list(), DefinedConf :: list()) -> allow | deny. -check(_Params, []) -> - %ct:pal("check auth_result: deny~n"), - deny; -check(Params, [ConfRecord|T]) -> - % ct:pal("Params: ~p, ConfRecord:~p ~n", [Params, ConfRecord]), - case match_config(Params, ConfRecord) of - not_match -> - check(Params, T); - matched -> allow - end. - -match_config([], _ConfigColumn) -> - %ct:pal("match_config auth_result: matched~n"), - matched; - -match_config([Param|T], ConfigColumn) -> - %ct:pal("Param: ~p, ConfigColumn:~p ~n", [Param, ConfigColumn]), - case lists:member(Param, ConfigColumn) of - true -> - match_config(T, ConfigColumn); - false -> - not_match - end. diff --git a/apps/emqx_auth_jwt/.gitignore b/apps/emqx_auth_jwt/.gitignore deleted file mode 100644 index 62e4fbb25..000000000 --- a/apps/emqx_auth_jwt/.gitignore +++ /dev/null @@ -1,28 +0,0 @@ -.eunit -deps -*.o -*.beam -*.plt -erl_crash.dump -ebin -rel/example_project -.concrete/DEV_MODE -.rebar -.erlang.mk/ -emqx_auth_jwt.d -data/ -.DS_Store -cover/ -ct.coverdata -eunit.coverdata -logs/ -test/ct.cover.spec -emq_auth_jwt.d -erlang.mk -_build/ -rebar.lock -rebar3.crashdump -etc/emqx_auth_jwt.conf.rendered -.rebar3/ -*.swp -Mnesia.nonode@nohost/ diff --git a/apps/emqx_auth_jwt/README.md b/apps/emqx_auth_jwt/README.md deleted file mode 100644 index 9675ae87c..000000000 --- a/apps/emqx_auth_jwt/README.md +++ /dev/null @@ -1,90 +0,0 @@ - -# emqx-auth-jwt - -EMQ X JWT Authentication Plugin - -Build ------ - -``` -make && make tests -``` - -Configure the Plugin --------------------- - -File: etc/plugins/emqx_auth_jwt.conf - -``` -## HMAC Hash Secret. -## -## Value: String -auth.jwt.secret = emqxsecret - -## From where the JWT string can be got -## -## Value: username | password -## Default: password -auth.jwt.from = password - -## RSA or ECDSA public key file. -## -## Value: File -## auth.jwt.pubkey = etc/certs/jwt_public_key.pem - -## Enable to verify claims fields -## -## Value: on | off -auth.jwt.verify_claims = off - -## The checklist of claims to validate -## -## Value: String -## auth.jwt.verify_claims.$name = expected -## -## Variables: -## - %u: username -## - %c: clientid -# auth.jwt.verify_claims.username = %u -``` - -Load the Plugin ---------------- - -``` -./bin/emqx_ctl plugins load emqx_auth_jwt -``` - -Example -------- - -``` -mosquitto_pub -t 'pub' -m 'hello' -i test -u test -P eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiYm9iIiwiYWdlIjoyOX0.bIV_ZQ8D5nQi0LT8AVkpM4Pd6wmlbpR9S8nOLJAsA8o -``` - -Algorithms ----------- - -The JWT spec supports several algorithms for cryptographic signing. This plugin currently supports: - -* HS256 - HMAC using SHA-256 hash algorithm -* HS384 - HMAC using SHA-384 hash algorithm -* HS512 - HMAC using SHA-512 hash algorithm - -* RS256 - RSA with the SHA-256 hash algorithm -* RS384 - RSA with the SHA-384 hash algorithm -* RS512 - RSA with the SHA-512 hash algorithm - -* ES256 - ECDSA using the P-256 curve -* ES384 - ECDSA using the P-384 curve -* ES512 - ECDSA using the P-512 curve - -License -------- - -Apache License Version 2.0 - -Author ------- - -EMQ X Team. diff --git a/apps/emqx_auth_jwt/TODO.md b/apps/emqx_auth_jwt/TODO.md deleted file mode 100644 index dfd730e0a..000000000 --- a/apps/emqx_auth_jwt/TODO.md +++ /dev/null @@ -1,2 +0,0 @@ -1. Notice for the [Critical vulnerabilities in JSON Web Token](https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/) - diff --git a/apps/emqx_auth_jwt/doc/hmac-vs-ecdsa-for-jwt.txt b/apps/emqx_auth_jwt/doc/hmac-vs-ecdsa-for-jwt.txt deleted file mode 100644 index 88fa5ebde..000000000 --- a/apps/emqx_auth_jwt/doc/hmac-vs-ecdsa-for-jwt.txt +++ /dev/null @@ -1,3 +0,0 @@ - -https://crypto.stackexchange.com/questions/30657/hmac-vs-ecdsa-for-jwt - diff --git a/apps/emqx_auth_jwt/etc/emqx_auth_jwt.conf b/apps/emqx_auth_jwt/etc/emqx_auth_jwt.conf deleted file mode 100644 index e0e6d48dd..000000000 --- a/apps/emqx_auth_jwt/etc/emqx_auth_jwt.conf +++ /dev/null @@ -1,49 +0,0 @@ -##-------------------------------------------------------------------- -## JWT Auth Plugin -##-------------------------------------------------------------------- - -## HMAC Hash Secret. -## -## Value: String -auth.jwt.secret = emqxsecret - -## RSA or ECDSA public key file. -## -## Value: File -#auth.jwt.pubkey = "etc/certs/jwt_public_key.pem" - -## The JWKs server address -## -## see: http://self-issued.info/docs/draft-ietf-jose-json-web-key.html -## -#auth.jwt.jwks.endpoint = "https://127.0.0.1:8080/jwks" - -## The JWKs refresh interval -## -## Value: Duration -#auth.jwt.jwks.refresh_interval = 5m - -## From where the JWT string can be got -## -## Value: username | password -## Default: password -auth.jwt.from = password - -## Enable to verify claims fields -## -## Value: on | off -auth.jwt.verify_claims.enable = off - -## The checklist of claims to validate -## -## Configuration format: auth.jwt.verify_claims.$name = $expected -## - $name: the name of the field in the JWT payload to be verified -## - $expected: the expected value -## -## The available placeholders for $expected: -## - %u: username -## - %c: clientid -## -## For example, to verify that the username in the JWT payload is the same -## as the client (MQTT protocol) username -#auth.jwt.verify_claims.username = "%u" \ No newline at end of file diff --git a/apps/emqx_auth_jwt/priv/emqx_auth_jwt.schema b/apps/emqx_auth_jwt/priv/emqx_auth_jwt.schema deleted file mode 100644 index 10b2daa5e..000000000 --- a/apps/emqx_auth_jwt/priv/emqx_auth_jwt.schema +++ /dev/null @@ -1,49 +0,0 @@ -%%-*- mode: erlang -*- - -{mapping, "auth.jwt.secret", "emqx_auth_jwt.secret", [ - {datatype, string} -]}. - -{mapping, "auth.jwt.jwks.endpoint", "emqx_auth_jwt.jwks", [ - {datatype, string} -]}. - -{mapping, "auth.jwt.jwks.refresh_interval", "emqx_auth_jwt.refresh_interval", [ - {datatype, {duration, ms}} -]}. - -{mapping, "auth.jwt.from", "emqx_auth_jwt.from", [ - {default, password}, - {datatype, atom} -]}. - -{mapping, "auth.jwt.pubkey", "emqx_auth_jwt.pubkey", [ - {datatype, string} -]}. - -{mapping, "auth.jwt.signature_format", "emqx_auth_jwt.jwerl_opts", [ - {default, "der"}, - {datatype, {enum, [raw, der]}} -]}. - -{mapping, "auth.jwt.verify_claims.enable", "emqx_auth_jwt.verify_claims", [ - {default, off}, - {datatype, flag} -]}. - -{mapping, "auth.jwt.verify_claims.$name", "emqx_auth_jwt.verify_claims", [ - {datatype, string} -]}. - -{translation, "emqx_auth_jwt.verify_claims", fun(Conf) -> - case cuttlefish:conf_get("auth.jwt.verify_claims.enable", Conf) of - false -> cuttlefish:unset(); - true -> - lists:foldr( - fun({["auth","jwt","verify_claims", Name], Value}, Acc) -> - [{list_to_atom(Name), list_to_binary(Value)} | Acc]; - ({["auth","jwt","verify_claims"], _Value}, Acc) -> - Acc - end, [], cuttlefish_variable:filter_by_prefix("auth.jwt.verify_claims", Conf)) - end -end}. diff --git a/apps/emqx_auth_jwt/rebar.config b/apps/emqx_auth_jwt/rebar.config deleted file mode 100644 index 3ec554950..000000000 --- a/apps/emqx_auth_jwt/rebar.config +++ /dev/null @@ -1,25 +0,0 @@ -{deps, - [ - {jose, {git, "https://github.com/potatosalad/erlang-jose", {tag, "1.11.1"}}} - ]}. - -{edoc_opts, [{preprocess, true}]}. -{erl_opts, [warn_unused_vars, - warn_shadow_vars, - warn_unused_import, - warn_obsolete_guard, - debug_info, - {parse_transform}]}. - -{xref_checks, [undefined_function_calls, undefined_functions, - locals_not_used, deprecated_function_calls, - warnings_as_errors, deprecated_functions]}. -{cover_enabled, true}. -{cover_opts, [verbose]}. -{cover_export_enabled, true}. - -{profiles, - [{test, - [{deps, []} - ]} - ]}. diff --git a/apps/emqx_auth_jwt/src/emqx_auth_jwt.app.src b/apps/emqx_auth_jwt/src/emqx_auth_jwt.app.src deleted file mode 100644 index 7d784e3b2..000000000 --- a/apps/emqx_auth_jwt/src/emqx_auth_jwt.app.src +++ /dev/null @@ -1,14 +0,0 @@ -{application, emqx_auth_jwt, - [{description, "EMQ X Authentication with JWT"}, - {vsn, "4.4.0"}, % strict semver, bump manually! - {modules, []}, - {registered, [emqx_auth_jwt_sup]}, - {applications, [kernel,stdlib,jose]}, - {mod, {emqx_auth_jwt_app, []}}, - {env, []}, - {licenses, ["Apache-2.0"]}, - {maintainers, ["EMQ X Team "]}, - {links, [{"Homepage", "https://emqx.io/"}, - {"Github", "https://github.com/emqx/emqx-auth-jwt"} - ]} - ]}. diff --git a/apps/emqx_auth_jwt/src/emqx_auth_jwt.appup.src b/apps/emqx_auth_jwt/src/emqx_auth_jwt.appup.src deleted file mode 100644 index b9831bb6f..000000000 --- a/apps/emqx_auth_jwt/src/emqx_auth_jwt.appup.src +++ /dev/null @@ -1,15 +0,0 @@ -%% -*-: erlang -*- -{VSN, - [ - {"4.3.0", [ - {load_module, emqx_auth_jwt_svr, brutal_purge, soft_purge, []} - ]}, - {<<".*">>, []} - ], - [ - {"4.3.0", [ - {load_module, emqx_auth_jwt_svr, brutal_purge, soft_purge, []} - ]}, - {<<".*">>, []} - ] -}. diff --git a/apps/emqx_auth_jwt/src/emqx_auth_jwt.erl b/apps/emqx_auth_jwt/src/emqx_auth_jwt.erl deleted file mode 100644 index ba37eac2b..000000000 --- a/apps/emqx_auth_jwt/src/emqx_auth_jwt.erl +++ /dev/null @@ -1,99 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_auth_jwt). - --include_lib("emqx/include/emqx.hrl"). --include_lib("emqx/include/logger.hrl"). - --logger_header("[JWT]"). - --export([ register_metrics/0 - , check/3 - , description/0 - ]). - --record(auth_metrics, { - success = 'client.auth.success', - failure = 'client.auth.failure', - ignore = 'client.auth.ignore' - }). - --define(METRICS(Type), tl(tuple_to_list(#Type{}))). --define(METRICS(Type, K), #Type{}#Type.K). - --define(AUTH_METRICS, ?METRICS(auth_metrics)). --define(AUTH_METRICS(K), ?METRICS(auth_metrics, K)). - --spec(register_metrics() -> ok). -register_metrics() -> - lists:foreach(fun emqx_metrics:ensure/1, ?AUTH_METRICS). - -%%-------------------------------------------------------------------- -%% Authentication callbacks -%%-------------------------------------------------------------------- - -check(ClientInfo, AuthResult, #{pid := Pid, - from := From, - checklists := Checklists}) -> - case maps:find(From, ClientInfo) of - error -> - ok = emqx_metrics:inc(?AUTH_METRICS(ignore)); - {ok, undefined} -> - ok = emqx_metrics:inc(?AUTH_METRICS(ignore)); - {ok, Token} -> - case emqx_auth_jwt_svr:verify(Pid, Token) of - {error, not_found} -> - ok = emqx_metrics:inc(?AUTH_METRICS(ignore)); - {error, not_token} -> - ok = emqx_metrics:inc(?AUTH_METRICS(ignore)); - {error, Reason} -> - ok = emqx_metrics:inc(?AUTH_METRICS(failure)), - {stop, AuthResult#{auth_result => Reason, anonymous => false}}; - {ok, Claims} -> - {stop, maps:merge(AuthResult, verify_claims(Checklists, Claims, ClientInfo))} - end - end. - -description() -> "Authentication with JWT". - -%%------------------------------------------------------------------------------ -%% Verify Claims -%%-------------------------------------------------------------------- - -verify_claims(Checklists, Claims, ClientInfo) -> - case do_verify_claims(feedvar(Checklists, ClientInfo), Claims) of - {error, Reason} -> - ok = emqx_metrics:inc(?AUTH_METRICS(failure)), - #{auth_result => Reason, anonymous => false}; - ok -> - ok = emqx_metrics:inc(?AUTH_METRICS(success)), - #{auth_result => success, anonymous => false, jwt_claims => Claims} - end. - -do_verify_claims([], _Claims) -> - ok; -do_verify_claims([{Key, Expected} | L], Claims) -> - case maps:get(Key, Claims, undefined) =:= Expected of - true -> do_verify_claims(L, Claims); - false -> {error, {verify_claim_failed, Key}} - end. - -feedvar(Checklists, #{username := Username, clientid := ClientId}) -> - lists:map(fun({K, <<"%u">>}) -> {K, Username}; - ({K, <<"%c">>}) -> {K, ClientId}; - ({K, Expected}) -> {K, Expected} - end, Checklists). diff --git a/apps/emqx_auth_jwt/src/emqx_auth_jwt_app.erl b/apps/emqx_auth_jwt/src/emqx_auth_jwt_app.erl deleted file mode 100644 index e501b0af4..000000000 --- a/apps/emqx_auth_jwt/src/emqx_auth_jwt_app.erl +++ /dev/null @@ -1,81 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_auth_jwt_app). - --behaviour(application). - --behaviour(supervisor). - --emqx_plugin(auth). - --export([start/2, stop/1]). - --export([init/1]). - --define(APP, emqx_auth_jwt). - -start(_Type, _Args) -> - {ok, Sup} = supervisor:start_link({local, ?MODULE}, ?MODULE, []), - - {ok, Pid} = start_auth_server(jwks_svr_options()), - ok = emqx_auth_jwt:register_metrics(), - AuthEnv0 = auth_env(), - AuthEnv1 = AuthEnv0#{pid => Pid}, - - _ = emqx:hook('client.authenticate', {emqx_auth_jwt, check, [AuthEnv1]}), - {ok, Sup, AuthEnv1}. - -stop(AuthEnv) -> - emqx:unhook('client.authenticate', {emqx_auth_jwt, check, [AuthEnv]}). - -%%-------------------------------------------------------------------- -%% Dummy supervisor -%%-------------------------------------------------------------------- - -init([]) -> - {ok, {{one_for_all, 1, 10}, []}}. - -start_auth_server(Options) -> - Spec = #{id => jwt_svr, - start => {emqx_auth_jwt_svr, start_link, [Options]}, - restart => permanent, - shutdown => brutal_kill, - type => worker, - modules => [emqx_auth_jwt_svr]}, - supervisor:start_child(?MODULE, Spec). - -%%-------------------------------------------------------------------- -%% Internal functions -%%-------------------------------------------------------------------- - -auth_env() -> - Checklists = [{atom_to_binary(K, utf8), V} - || {K, V} <- env(verify_claims, [])], - #{ from => env(from, password) - , checklists => Checklists - }. - -jwks_svr_options() -> - [{K, V} || {K, V} - <- [{secret, env(secret, undefined)}, - {pubkey, env(pubkey, undefined)}, - {jwks_addr, env(jwks, undefined)}, - {interval, env(refresh_interval, undefined)}], - V /= undefined]. - -env(Key, Default) -> - application:get_env(?APP, Key, Default). diff --git a/apps/emqx_auth_jwt/src/emqx_auth_jwt_svr.erl b/apps/emqx_auth_jwt/src/emqx_auth_jwt_svr.erl deleted file mode 100644 index b9d19bf57..000000000 --- a/apps/emqx_auth_jwt/src/emqx_auth_jwt_svr.erl +++ /dev/null @@ -1,224 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_auth_jwt_svr). - --behaviour(gen_server). - --include_lib("emqx/include/logger.hrl"). --include_lib("jose/include/jose_jwk.hrl"). - --logger_header("[JWT-SVR]"). - -%% APIs --export([start_link/1]). - --export([verify/2]). - -%% gen_server callbacks --export([ init/1 - , handle_call/3 - , handle_cast/2 - , handle_info/2 - , terminate/2 - , code_change/3 - ]). - --type options() :: [option()]. --type option() :: {secret, list()} - | {pubkey, list()} - | {jwks_addr, list()} - | {interval, pos_integer()}. - --define(INTERVAL, 300000). - --record(state, {static, remote, addr, tref, intv}). - -%%-------------------------------------------------------------------- -%% APIs -%%-------------------------------------------------------------------- - --spec start_link(options()) -> gen_server:start_ret(). -start_link(Options) -> - gen_server:start_link(?MODULE, [Options], []). - --spec verify(pid(), binary()) - -> {error, term()} - | {ok, Payload :: map()}. -verify(S, JwsCompacted) when is_binary(JwsCompacted) -> - case catch jose_jws:peek(JwsCompacted) of - {'EXIT', _} -> {error, not_token}; - _ -> gen_server:call(S, {verify, JwsCompacted}) - end. - -%%-------------------------------------------------------------------- -%% gen_server callbacks -%%-------------------------------------------------------------------- - -init([Options]) -> - ok = jose:json_module(jiffy), - {Static, Remote} = do_init_jwks(Options), - Intv = proplists:get_value(interval, Options, ?INTERVAL), - {ok, reset_timer( - #state{ - static = Static, - remote = Remote, - addr = proplists:get_value(jwks_addr, Options), - intv = Intv})}. - -%% @private -do_init_jwks(Options) -> - K2J = fun(K, F) -> - case proplists:get_value(K, Options) of - undefined -> undefined; - V -> - try F(V) of - {error, Reason} -> - ?LOG(warning, "Build ~p JWK ~p failed: {error, ~p}~n", - [K, V, Reason]), - undefined; - J -> J - catch T:R:_ -> - ?LOG(warning, "Build ~p JWK ~p failed: {~p, ~p}~n", - [K, V, T, R]), - undefined - end - end - end, - OctJwk = K2J(secret, fun(V) -> - jose_jwk:from_oct(list_to_binary(V)) - end), - PemJwk = K2J(pubkey, fun jose_jwk:from_pem_file/1), - Remote = K2J(jwks_addr, fun request_jwks/1), - {[J ||J <- [OctJwk, PemJwk], J /= undefined], Remote}. - -handle_call({verify, JwsCompacted}, _From, State) -> - handle_verify(JwsCompacted, State); - -handle_call(_Req, _From, State) -> - {reply, ok, State}. - -handle_cast(_Msg, State) -> - {noreply, State}. - -handle_info({timeout, _TRef, refresh}, State = #state{addr = Addr}) -> - NState = try - State#state{remote = request_jwks(Addr)} - catch _:_ -> - State - end, - {noreply, reset_timer(NState)}; - -handle_info(_Info, State) -> - {noreply, State}. - -terminate(_Reason, State) -> - _ = cancel_timer(State), - ok. - -code_change(_OldVsn, State, _Extra) -> - {ok, State}. - -%%-------------------------------------------------------------------- -%% Internal funcs -%%-------------------------------------------------------------------- - -handle_verify(JwsCompacted, - State = #state{static = Static, remote = Remote}) -> - try - Jwks = case emqx_json:decode(jose_jws:peek_protected(JwsCompacted), [return_maps]) of - #{<<"kid">> := Kid} when Remote /= undefined -> - [J || J <- Remote, maps:get(<<"kid">>, J#jose_jwk.fields, undefined) =:= Kid]; - _ -> Static - end, - case Jwks of - [] -> {reply, {error, not_found}, State}; - _ -> - {reply, do_verify(JwsCompacted, Jwks), State} - end - catch - Class : Reason : Stk -> - ?LOG(error, "Handle JWK crashed: ~p, ~p, stacktrace: ~p~n", - [Class, Reason, Stk]), - {reply, {error, invalid_signature}, State} - end. - -request_jwks(Addr) -> - case httpc:request(get, {Addr, []}, [], [{body_format, binary}]) of - {error, Reason} -> - error(Reason); - {ok, {_Code, _Headers, Body}} -> - try - JwkSet = jose_jwk:from(emqx_json:decode(Body, [return_maps])), - {_, Jwks} = JwkSet#jose_jwk.keys, Jwks - catch _:_ -> - ?LOG(error, "Invalid jwks server response: ~p~n", [Body]), - error(badarg) - end - end. - -reset_timer(State = #state{addr = undefined}) -> - State; -reset_timer(State = #state{intv = Intv}) -> - State#state{tref = erlang:start_timer(Intv, self(), refresh)}. - -cancel_timer(State = #state{tref = undefined}) -> - State; -cancel_timer(State = #state{tref = TRef}) -> - _ = erlang:cancel_timer(TRef), - State#state{tref = undefined}. - -do_verify(_JwsCompated, []) -> - {error, invalid_signature}; -do_verify(JwsCompacted, [Jwk|More]) -> - case jose_jws:verify(Jwk, JwsCompacted) of - {true, Payload, _Jws} -> - Claims = emqx_json:decode(Payload, [return_maps]), - case check_claims(Claims) of - {false, <<"exp">>} -> - {error, {invalid_signature, expired}}; - NClaims -> - {ok, NClaims} - end; - {false, _, _} -> - do_verify(JwsCompacted, More) - end. - -check_claims(Claims) -> - Now = os:system_time(seconds), - Checker = [{<<"exp">>, fun(ExpireTime) -> - Now < ExpireTime - end}, - {<<"iat">>, fun(IssueAt) -> - IssueAt =< Now - end}, - {<<"nbf">>, fun(NotBefore) -> - NotBefore =< Now - end} - ], - do_check_claim(Checker, Claims). - -do_check_claim([], Claims) -> - Claims; -do_check_claim([{K, F}|More], Claims) -> - case maps:take(K, Claims) of - error -> do_check_claim(More, Claims); - {V, NClaims} -> - case F(V) of - true -> do_check_claim(More, NClaims); - _ -> {false, K} - end - end. diff --git a/apps/emqx_auth_jwt/test/emqx_auth_jwt_SUITE.erl b/apps/emqx_auth_jwt/test/emqx_auth_jwt_SUITE.erl deleted file mode 100644 index d4f562b6f..000000000 --- a/apps/emqx_auth_jwt/test/emqx_auth_jwt_SUITE.erl +++ /dev/null @@ -1,166 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_auth_jwt_SUITE). - --compile(export_all). --compile(nowarn_export_all). - --include_lib("emqx/include/emqx.hrl"). --include_lib("eunit/include/eunit.hrl"). --include_lib("common_test/include/ct.hrl"). - --define(APP, emqx_auth_jwt). - -all() -> - [{group, emqx_auth_jwt}]. - -groups() -> - [{emqx_auth_jwt, [sequence], [ t_check_auth - , t_check_claims - , t_check_claims_clientid - , t_check_claims_username - , t_check_claims_kid_in_header - ]} - ]. - -init_per_suite(Config) -> - emqx_ct_helpers:start_apps([emqx_auth_jwt], fun set_special_configs/1), - Config. - -end_per_suite(_Config) -> - emqx_ct_helpers:stop_apps([emqx_auth_jwt]). - -set_special_configs(emqx) -> - application:set_env(emqx, allow_anonymous, false), - application:set_env(emqx, acl_nomatch, deny), - application:set_env(emqx, enable_acl_cache, false), - LoadedPluginPath = filename:join(["test", "emqx_SUITE_data", "loaded_plugins"]), - AclFilePath = filename:join(["test", "emqx_SUITE_data", "acl.conf"]), - application:set_env(emqx, plugins_loaded_file, - emqx_ct_helpers:deps_path(emqx, LoadedPluginPath)), - application:set_env(emqx, acl_file, - emqx_ct_helpers:deps_path(emqx, AclFilePath)); - -set_special_configs(emqx_auth_jwt) -> - application:set_env(emqx_auth_jwt, secret, "emqxsecret"), - application:set_env(emqx_auth_jwt, from, password); - -set_special_configs(_) -> - ok. - -sign(Payload, Header, Key) when is_map(Header) -> - Jwk = jose_jwk:from_oct(Key), - Jwt = emqx_json:encode(Payload), - {_, Token} = jose_jws:compact(jose_jwt:sign(Jwk, Header, Jwt)), - Token; - -sign(Payload, Alg, Key) -> - Jwk = jose_jwk:from_oct(Key), - Jwt = emqx_json:encode(Payload), - {_, Token} = jose_jws:compact(jose_jwt:sign(Jwk, #{<<"alg">> => Alg}, Jwt)), - Token. - -%%------------------------------------------------------------------------------ -%% Testcases -%%------------------------------------------------------------------------------ - -t_check_auth(_) -> - Plain = #{clientid => <<"client1">>, username => <<"plain">>, zone => external}, - Jwt = sign([{clientid, <<"client1">>}, - {username, <<"plain">>}, - {exp, os:system_time(seconds) + 3}], <<"HS256">>, <<"emqxsecret">>), - ct:pal("Jwt: ~p~n", [Jwt]), - - Result0 = emqx_access_control:authenticate(Plain#{password => Jwt}), - ct:pal("Auth result: ~p~n", [Result0]), - ?assertMatch({ok, #{auth_result := success, jwt_claims := #{<<"clientid">> := <<"client1">>}}}, Result0), - - ct:sleep(3100), - Result1 = emqx_access_control:authenticate(Plain#{password => Jwt}), - ct:pal("Auth result after 1000ms: ~p~n", [Result1]), - ?assertMatch({error, _}, Result1), - - Jwt_Error = sign([{client_id, <<"client1">>}, - {username, <<"plain">>}], <<"HS256">>, <<"secret">>), - ct:pal("invalid jwt: ~p~n", [Jwt_Error]), - Result2 = emqx_access_control:authenticate(Plain#{password => Jwt_Error}), - ct:pal("Auth result for the invalid jwt: ~p~n", [Result2]), - ?assertEqual({error, invalid_signature}, Result2), - ?assertMatch({error, _}, emqx_access_control:authenticate(Plain#{password => <<"asd">>})). - -t_check_claims(_) -> - application:set_env(emqx_auth_jwt, verify_claims, [{sub, <<"value">>}]), - application:stop(emqx_auth_jwt), application:start(emqx_auth_jwt), - - Plain = #{clientid => <<"client1">>, username => <<"plain">>, zone => external}, - Jwt = sign([{client_id, <<"client1">>}, - {username, <<"plain">>}, - {sub, value}, - {exp, os:system_time(seconds) + 3}], <<"HS256">>, <<"emqxsecret">>), - Result0 = emqx_access_control:authenticate(Plain#{password => Jwt}), - ct:pal("Auth result: ~p~n", [Result0]), - ?assertMatch({ok, #{auth_result := success, jwt_claims := _}}, Result0), - Jwt_Error = sign([{clientid, <<"client1">>}, - {username, <<"plain">>}], <<"HS256">>, <<"secret">>), - Result2 = emqx_access_control:authenticate(Plain#{password => Jwt_Error}), - ct:pal("Auth result for the invalid jwt: ~p~n", [Result2]), - ?assertEqual({error, invalid_signature}, Result2). - -t_check_claims_clientid(_) -> - application:set_env(emqx_auth_jwt, verify_claims, [{clientid, <<"%c">>}]), - application:stop(emqx_auth_jwt), application:start(emqx_auth_jwt), - Plain = #{clientid => <<"client23">>, username => <<"plain">>, zone => external}, - Jwt = sign([{clientid, <<"client23">>}, - {username, <<"plain">>}, - {exp, os:system_time(seconds) + 3}], <<"HS256">>, <<"emqxsecret">>), - Result0 = emqx_access_control:authenticate(Plain#{password => Jwt}), - ct:pal("Auth result: ~p~n", [Result0]), - ?assertMatch({ok, #{auth_result := success, jwt_claims := _}}, Result0), - Jwt_Error = sign([{clientid, <<"client1">>}, - {username, <<"plain">>}], <<"HS256">>, <<"secret">>), - Result2 = emqx_access_control:authenticate(Plain#{password => Jwt_Error}), - ct:pal("Auth result for the invalid jwt: ~p~n", [Result2]), - ?assertEqual({error, invalid_signature}, Result2). - -t_check_claims_username(_) -> - application:set_env(emqx_auth_jwt, verify_claims, [{username, <<"%u">>}]), - application:stop(emqx_auth_jwt), application:start(emqx_auth_jwt), - - Plain = #{clientid => <<"client23">>, username => <<"plain">>, zone => external}, - Jwt = sign([{client_id, <<"client23">>}, - {username, <<"plain">>}, - {exp, os:system_time(seconds) + 3}], <<"HS256">>, <<"emqxsecret">>), - Result0 = emqx_access_control:authenticate(Plain#{password => Jwt}), - ct:pal("Auth result: ~p~n", [Result0]), - ?assertMatch({ok, #{auth_result := success, jwt_claims := _}}, Result0), - Jwt_Error = sign([{clientid, <<"client1">>}, - {username, <<"plain">>}], <<"HS256">>, <<"secret">>), - Result3 = emqx_access_control:authenticate(Plain#{password => Jwt_Error}), - ct:pal("Auth result for the invalid jwt: ~p~n", [Result3]), - ?assertEqual({error, invalid_signature}, Result3). - -t_check_claims_kid_in_header(_) -> - application:set_env(emqx_auth_jwt, verify_claims, []), - Plain = #{clientid => <<"client23">>, username => <<"plain">>, zone => external}, - Jwt = sign([{clientid, <<"client23">>}, - {username, <<"plain">>}, - {exp, os:system_time(seconds) + 3}], - #{<<"alg">> => <<"HS256">>, - <<"kid">> => <<"a_kid_str">>}, <<"emqxsecret">>), - Result0 = emqx_access_control:authenticate(Plain#{password => Jwt}), - ct:pal("Auth result: ~p~n", [Result0]), - ?assertMatch({ok, #{auth_result := success, jwt_claims := _}}, Result0). diff --git a/apps/emqx_auth_ldap/.gitignore b/apps/emqx_auth_ldap/.gitignore deleted file mode 100644 index eb8f0639f..000000000 --- a/apps/emqx_auth_ldap/.gitignore +++ /dev/null @@ -1,25 +0,0 @@ -.eunit -deps -*.o -*.beam -*.plt -erl_crash.dump -ebin -rel/example_project -.concrete/DEV_MODE -.rebar -.erlang.mk/ -emqx_auth_ldap.d -data/ -cover/ -ct.coverdata -eunit.coverdata -logs/ -test/ct.cover.spec -.DS_Store -_build/ -rebar.lock -erlang.mk -rebar3.crashdump -.rebar3/ -etc/emqx_auth_ldap.conf.rendered diff --git a/apps/emqx_auth_ldap/README.md b/apps/emqx_auth_ldap/README.md deleted file mode 100644 index c4d56c839..000000000 --- a/apps/emqx_auth_ldap/README.md +++ /dev/null @@ -1,96 +0,0 @@ -emqx_auth_ldap -============== - -EMQ X LDAP Authentication Plugin - -Build ------ - -``` -make -``` - -Load the Plugin ---------------- - -``` -# ./bin/emqx_ctl plugins load emqx_auth_ldap -``` - -Generate Password ---------------- - -``` -slappasswd -h '{ssha}' -s public -``` - -Configuration Open LDAP ------------------------ - -vim /etc/openldap/slapd.conf - -``` -include /etc/openldap/schema/core.schema -include /etc/openldap/schema/cosine.schema -include /etc/openldap/schema/inetorgperson.schema -include /etc/openldap/schema/ppolicy.schema -include /etc/openldap/schema/emqx.schema - -database bdb -suffix "dc=emqx,dc=io" -rootdn "cn=root,dc=emqx,dc=io" -rootpw {SSHA}eoF7NhNrejVYYyGHqnt+MdKNBh4r1w3W - -directory /etc/openldap/data -``` - -If the ldap launched with error below: -``` -Unrecognized database type (bdb) -5c4a72b9 slapd.conf: line 7: failed init (bdb) -slapadd: bad configuration file! -``` - -Insert lines to the slapd.conf -``` -modulepath /usr/lib/ldap -moduleload back_bdb.la -``` - -Import EMQX User Data ----------------------- - -Use ldapadd -``` -# ldapadd -x -D "cn=root,dc=emqx,dc=io" -w public -f emqx.com.ldif -``` - -Use slapadd -``` -# sudo slapadd -l schema/emqx.io.ldif -f slapd.conf -``` - -Launch slapd -``` -# sudo slapd -d 3 -``` - -Test ------ -After configure slapd correctly and launch slapd successfully. -You could execute - -``` bash -# make tests -``` - -License -------- - -Apache License Version 2.0 - -Author ------- - -EMQ X Team. - diff --git a/apps/emqx_auth_ldap/emqx.io.ldif b/apps/emqx_auth_ldap/emqx.io.ldif deleted file mode 100644 index f9833cd88..000000000 --- a/apps/emqx_auth_ldap/emqx.io.ldif +++ /dev/null @@ -1,135 +0,0 @@ -## create emqx.io - -dn:dc=emqx,dc=io -objectclass: top -objectclass: dcobject -objectclass: organization -dc:emqx -o:emqx,Inc. - -# create testdevice.emqx.io -dn:ou=testdevice,dc=emqx,dc=io -objectClass: top -objectclass:organizationalUnit -ou:testdevice - -# create user admin -dn:uid=admin,ou=testdevice,dc=emqx,dc=io -objectClass: top -objectClass: simpleSecurityObject -objectClass: account -userPassword:: e1NIQX1XNnBoNU1tNVB6OEdnaVVMYlBnekczN21qOWc9 -uid: admin - -## create user=mqttuser0001, -# password=mqttuser0001, -# passhash={SHA}mlb3fat40MKBTXUVZwCKmL73R/0= -# base64passhash=e1NIQX1tbGIzZmF0NDBNS0JUWFVWWndDS21MNzNSLzA9 -dn:uid=mqttuser0001,ou=testdevice,dc=emqx,dc=io -objectClass: top -objectClass: mqttUser -objectClass: mqttDevice -objectClass: mqttSecurity -uid: mqttuser0001 -isEnabled: TRUE -mqttAccountName: user1 -mqttPublishTopic: mqttuser0001/pub/1 -mqttPublishTopic: mqttuser0001/pub/+ -mqttPublishTopic: mqttuser0001/pub/# -mqttSubscriptionTopic: mqttuser0001/sub/1 -mqttSubscriptionTopic: mqttuser0001/sub/+ -mqttSubscriptionTopic: mqttuser0001/sub/# -mqttPubSubTopic: mqttuser0001/pubsub/1 -mqttPubSubTopic: mqttuser0001/pubsub/+ -mqttPubSubTopic: mqttuser0001/pubsub/# -userPassword:: e1NIQX1tbGIzZmF0NDBNS0JUWFVWWndDS21MNzNSLzA9 - -## create user=mqttuser0002 -# password=mqttuser0002, -# passhash={SSHA}n9XdtoG4Q/TQ3TQF4Y+khJbMBH4qXj4M -# base64passhash=e1NTSEF9bjlYZHRvRzRRL1RRM1RRRjRZK2toSmJNQkg0cVhqNE0= -dn:uid=mqttuser0002,ou=testdevice,dc=emqx,dc=io -objectClass: top -objectClass: mqttUser -objectClass: mqttDevice -objectClass: mqttSecurity -uid: mqttuser0002 -isEnabled: TRUE -mqttAccountName: user2 -mqttPublishTopic: mqttuser0002/pub/1 -mqttPublishTopic: mqttuser0002/pub/+ -mqttPublishTopic: mqttuser0002/pub/# -mqttSubscriptionTopic: mqttuser0002/sub/1 -mqttSubscriptionTopic: mqttuser0002/sub/+ -mqttSubscriptionTopic: mqttuser0002/sub/# -mqttPubSubTopic: mqttuser0002/pubsub/1 -mqttPubSubTopic: mqttuser0002/pubsub/+ -mqttPubSubTopic: mqttuser0002/pubsub/# -userPassword:: e1NTSEF9bjlYZHRvRzRRL1RRM1RRRjRZK2toSmJNQkg0cVhqNE0= - -## create user mqttuser0003 -# password=mqttuser0003, -# passhash={MD5}ybsPGoaK3nDyiQvveiCOIw== -# base64passhash=e01ENX15YnNQR29hSzNuRHlpUXZ2ZWlDT0l3PT0= -dn:uid=mqttuser0003,ou=testdevice,dc=emqx,dc=io -objectClass: top -objectClass: mqttUser -objectClass: mqttDevice -objectClass: mqttSecurity -uid: mqttuser0003 -isEnabled: TRUE -mqttPublishTopic: mqttuser0003/pub/1 -mqttPublishTopic: mqttuser0003/pub/+ -mqttPublishTopic: mqttuser0003/pub/# -mqttSubscriptionTopic: mqttuser0003/sub/1 -mqttSubscriptionTopic: mqttuser0003/sub/+ -mqttSubscriptionTopic: mqttuser0003/sub/# -mqttPubSubTopic: mqttuser0003/pubsub/1 -mqttPubSubTopic: mqttuser0003/pubsub/+ -mqttPubSubTopic: mqttuser0003/pubsub/# -userPassword:: e01ENX15YnNQR29hSzNuRHlpUXZ2ZWlDT0l3PT0= - -## create user mqttuser0004 -# password=mqttuser0004, -# passhash={MD5}2Br6pPDSEDIEvUlu9+s+MA== -# base64passhash=e01ENX0yQnI2cFBEU0VESUV2VWx1OStzK01BPT0= -dn:uid=mqttuser0004,ou=testdevice,dc=emqx,dc=io -objectClass: top -objectClass: mqttUser -objectClass: mqttDevice -objectClass: mqttSecurity -uid: mqttuser0004 -isEnabled: TRUE -mqttPublishTopic: mqttuser0004/pub/1 -mqttPublishTopic: mqttuser0004/pub/+ -mqttPublishTopic: mqttuser0004/pub/# -mqttSubscriptionTopic: mqttuser0004/sub/1 -mqttSubscriptionTopic: mqttuser0004/sub/+ -mqttSubscriptionTopic: mqttuser0004/sub/# -mqttPubSubTopic: mqttuser0004/pubsub/1 -mqttPubSubTopic: mqttuser0004/pubsub/+ -mqttPubSubTopic: mqttuser0004/pubsub/# -userPassword: {MD5}2Br6pPDSEDIEvUlu9+s+MA== - -## create user mqttuser0005 -# password=mqttuser0005, -# passhash={SHA}jKnxeEDGR14kE8AR7yuVFOelhz4= -# base64passhash=e1NIQX1qS254ZUVER1IxNGtFOEFSN3l1VkZPZWxoejQ9 -objectClass: top -dn:uid=mqttuser0005,ou=testdevice,dc=emqx,dc=io -objectClass: mqttUser -objectClass: mqttDevice -objectClass: mqttSecurity -uid: mqttuser0005 -isEnabled: TRUE -mqttPublishTopic: mqttuser0005/pub/1 -mqttPublishTopic: mqttuser0005/pub/+ -mqttPublishTopic: mqttuser0005/pub/# -mqttSubscriptionTopic: mqttuser0005/sub/1 -mqttSubscriptionTopic: mqttuser0005/sub/+ -mqttSubscriptionTopic: mqttuser0005/sub/# -mqttPubSubTopic: mqttuser0005/pubsub/1 -mqttPubSubTopic: mqttuser0005/pubsub/+ -mqttPubSubTopic: mqttuser0005/pubsub/# -userPassword: {SHA}jKnxeEDGR14kE8AR7yuVFOelhz4= - diff --git a/apps/emqx_auth_ldap/emqx.schema b/apps/emqx_auth_ldap/emqx.schema deleted file mode 100644 index 55f92269b..000000000 --- a/apps/emqx_auth_ldap/emqx.schema +++ /dev/null @@ -1,46 +0,0 @@ -# -# Preliminary Apple OS X Native LDAP Schema -# This file is subject to change. -# -attributetype ( 1.3.6.1.4.1.11.2.53.2.2.3.1.2.3.1.3 NAME 'isEnabled' - EQUALITY booleanMatch - SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 - SINGLE-VALUE - USAGE userApplications ) - -attributetype ( 1.3.6.1.4.1.11.2.53.2.2.3.1.2.3.4.1 NAME ( 'mqttPublishTopic' 'mpt' ) - EQUALITY caseIgnoreMatch - SUBSTR caseIgnoreSubstringsMatch - SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 - USAGE userApplications ) -attributetype ( 1.3.6.1.4.1.11.2.53.2.2.3.1.2.3.4.2 NAME ( 'mqttSubscriptionTopic' 'mst' ) - EQUALITY caseIgnoreMatch - SUBSTR caseIgnoreSubstringsMatch - SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 - USAGE userApplications ) -attributetype ( 1.3.6.1.4.1.11.2.53.2.2.3.1.2.3.4.3 NAME ( 'mqttPubSubTopic' 'mpst' ) - EQUALITY caseIgnoreMatch - SUBSTR caseIgnoreSubstringsMatch - SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 - USAGE userApplications ) -attributetype ( 1.3.6.1.4.1.11.2.53.2.2.3.1.2.3.4.4 NAME ( 'mqttAccountName' 'man' ) - EQUALITY caseIgnoreMatch - SUBSTR caseIgnoreSubstringsMatch - SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 - USAGE userApplications ) - - -objectclass ( 1.3.6.1.4.1.11.2.53.2.2.3.1.2.3.4 NAME 'mqttUser' - AUXILIARY - MAY ( mqttPublishTopic $ mqttSubscriptionTopic $ mqttPubSubTopic $ mqttAccountName) ) - -objectclass ( 1.3.6.1.4.1.11.2.53.2.2.3.1.2.3.2 NAME 'mqttDevice' - SUP top - STRUCTURAL - MUST ( uid ) - MAY ( isEnabled ) ) - -objectclass ( 1.3.6.1.4.1.11.2.53.2.2.3.1.2.3.3 NAME 'mqttSecurity' - SUP top - AUXILIARY - MAY ( userPassword $ userPKCS12 $ pwdAttribute $ pwdLockout ) ) diff --git a/apps/emqx_auth_ldap/etc/emqx_auth_ldap.conf b/apps/emqx_auth_ldap/etc/emqx_auth_ldap.conf deleted file mode 100644 index b457229e3..000000000 --- a/apps/emqx_auth_ldap/etc/emqx_auth_ldap.conf +++ /dev/null @@ -1,76 +0,0 @@ -##-------------------------------------------------------------------- -## LDAP Auth Plugin -##-------------------------------------------------------------------- - -## LDAP server list, seperated by ','. -## -## Value: String -auth.ldap.servers = "127.0.0.1" - -## LDAP server port. -## -## Value: Port -auth.ldap.port = 389 - -## LDAP pool size -## -## Value: String -auth.ldap.pool = 8 - -## LDAP Bind DN. -## -## Value: DN -auth.ldap.bind_dn = "cn=root,dc=emqx,dc=io" - -## LDAP Bind Password. -## -## Value: String -auth.ldap.bind_password = public - -## LDAP query timeout. -## -## Value: Number -auth.ldap.timeout = 30s - -## Device DN. -## -## Variables: -## -## Value: DN -auth.ldap.device_dn = "ou=device,dc=emqx,dc=io" - -## Specified ObjectClass -## -## Variables: -## -## Value: string -auth.ldap.match_objectclass = mqttUser - -## attributetype for username -## -## Variables: -## -## Value: string -auth.ldap.username.attributetype = uid - -## attributetype for password -## -## Variables: -## -## Value: string -auth.ldap.password.attributetype = userPassword - -## Whether to enable SSL. -## -## Value: true | false -auth.ldap.ssl.enable = false - -#auth.ldap.ssl.certfile = "etc/certs/cert.pem" - -#auth.ldap.ssl.keyfile = "etc/certs/key.pem" - -#auth.ldap.ssl.cacertfile = "etc/certs/cacert.pem" - -#auth.ldap.ssl.verify = "verify_peer" - -#auth.ldap.ssl.server_name_indication = your_server_name diff --git a/apps/emqx_auth_ldap/include/emqx_auth_ldap.hrl b/apps/emqx_auth_ldap/include/emqx_auth_ldap.hrl deleted file mode 100644 index 8950c0ec8..000000000 --- a/apps/emqx_auth_ldap/include/emqx_auth_ldap.hrl +++ /dev/null @@ -1,23 +0,0 @@ - --define(APP, emqx_auth_ldap). - --record(auth_metrics, { - success = 'client.auth.success', - failure = 'client.auth.failure', - ignore = 'client.auth.ignore' - }). - --record(acl_metrics, { - allow = 'client.acl.allow', - deny = 'client.acl.deny', - ignore = 'client.acl.ignore' - }). - --define(METRICS(Type), tl(tuple_to_list(#Type{}))). --define(METRICS(Type, K), #Type{}#Type.K). - --define(AUTH_METRICS, ?METRICS(auth_metrics)). --define(AUTH_METRICS(K), ?METRICS(auth_metrics, K)). - --define(ACL_METRICS, ?METRICS(acl_metrics)). --define(ACL_METRICS(K), ?METRICS(acl_metrics, K)). diff --git a/apps/emqx_auth_ldap/priv/emqx_auth_ldap.schema b/apps/emqx_auth_ldap/priv/emqx_auth_ldap.schema deleted file mode 100644 index f9c3bf16b..000000000 --- a/apps/emqx_auth_ldap/priv/emqx_auth_ldap.schema +++ /dev/null @@ -1,174 +0,0 @@ -%%-*- mode: erlang -*- -%% emqx_auth_ldap config mapping - -{mapping, "auth.ldap.servers", "emqx_auth_ldap.ldap", [ - {default, "127.0.0.1"}, - {datatype, string} -]}. - -{mapping, "auth.ldap.port", "emqx_auth_ldap.ldap", [ - {default, 389}, - {datatype, integer} -]}. - -{mapping, "auth.ldap.pool", "emqx_auth_ldap.ldap", [ - {default, 8}, - {datatype, integer} -]}. - -{mapping, "auth.ldap.bind_dn", "emqx_auth_ldap.ldap", [ - {datatype, string}, - {default, "cn=root,dc=emqx,dc=io"} -]}. - -{mapping, "auth.ldap.bind_password", "emqx_auth_ldap.ldap", [ - {datatype, string}, - {default, "public"} -]}. - -{mapping, "auth.ldap.timeout", "emqx_auth_ldap.ldap", [ - {default, "30s"}, - {datatype, {duration, ms}} -]}. - -{mapping, "auth.ldap.ssl.enable", "emqx_auth_ldap.ldap", [ - {default, false}, - {datatype, {enum, [true, false]}} -]}. - -{mapping, "auth.ldap.ssl.certfile", "emqx_auth_ldap.ldap", [ - {datatype, string} -]}. - -{mapping, "auth.ldap.ssl.keyfile", "emqx_auth_ldap.ldap", [ - {datatype, string} -]}. - -{mapping, "auth.ldap.ssl.cacertfile", "emqx_auth_ldap.ldap", [ - {datatype, string} -]}. - -{mapping, "auth.ldap.ssl.verify", "emqx_auth_ldap.ldap", [ - {default, verify_none}, - {datatype, {enum, [verify_none, verify_peer]}} -]}. - -{mapping, "auth.ldap.ssl.server_name_indication", "emqx_auth_ldap.ldap", [ - {datatype, string} -]}. - -{translation, "emqx_auth_ldap.ldap", fun(Conf) -> - A2N = fun(A) -> case inet:parse_address(A) of {ok, N} -> N; _ -> A end end, - Servers = [A2N(A) || A <- string:tokens(cuttlefish:conf_get("auth.ldap.servers", Conf), ",")], - Port = cuttlefish:conf_get("auth.ldap.port", Conf), - Pool = cuttlefish:conf_get("auth.ldap.pool", Conf), - BindDN = cuttlefish:conf_get("auth.ldap.bind_dn", Conf), - BindPassword = cuttlefish:conf_get("auth.ldap.bind_password", Conf), - Timeout = cuttlefish:conf_get("auth.ldap.timeout", Conf), - Filter = fun(Ls) -> [E || E = {_, V} <- Ls, V /= undefined]end, - SslOpts = fun() -> - [{certfile, cuttlefish:conf_get("auth.ldap.ssl.certfile", Conf)}, - {keyfile, cuttlefish:conf_get("auth.ldap.ssl.keyfile", Conf)}, - {cacertfile, cuttlefish:conf_get("auth.ldap.ssl.cacertfile", Conf, undefined)}, - {verify, cuttlefish:conf_get("auth.ldap.ssl.verify", Conf, undefined)}, - {server_name_indication, case cuttlefish:conf_get("auth.ldap.ssl.server_name_indication", Conf, undefined) of - "disable" -> disable; - SNI -> SNI - end}] - end, - Opts = [{servers, Servers}, - {port, Port}, - {timeout, Timeout}, - {bind_dn, BindDN}, - {bind_password, BindPassword}, - {pool, Pool}, - {auto_reconnect, 2}], - case cuttlefish:conf_get("auth.ldap.ssl.enable", Conf) of - true -> [{ssl, true}, {sslopts, Filter(SslOpts())}|Opts]; - false -> [{ssl, false}|Opts] - end -end}. - -{mapping, "auth.ldap.device_dn", "emqx_auth_ldap.device_dn", [ - {default, "ou=device,dc=emqx,dc=io"}, - {datatype, string} -]}. - -{mapping, "auth.ldap.match_objectclass", "emqx_auth_ldap.match_objectclass", [ - {default, "mqttUser"}, - {datatype, string} -]}. - -{mapping, "auth.ldap.custom_base_dn", "emqx_auth_ldap.custom_base_dn", [ - {default, "${username_attr}=${user},${device_dn}"}, - {datatype, string} -]}. - -%% auth.ldap.filters.1.key = "objectClass" -%% auth.ldap.filters.1.value = "mqttUser" -%% auth.ldap.filters.1.op = "and" -%% auth.ldap.filters.2.key = "uiAttr" -%% auth.ldap.filters.2.value "someAttr" -%% auth.ldap.filters.2.op = "or" -%% auth.ldap.filters.3.key = "someKey" -%% auth.ldap.filters.3.value = "someValue" -%% The configuratation structure sent to the application: -%% [{"objectClass","mqttUser"},"and",{"uiAttr","someAttr"},"or",{"someKey","someAttr"}] -%% The resulting LDAP filter would look like this: -%% ==> "|(&(objectClass=Class)(uiAttr=someAttr)(someKey=someValue))" -{translation, "emqx_auth_ldap.filters", -fun(Conf) -> - Settings = cuttlefish_variable:filter_by_prefix("auth.ldap.filters", Conf), - Keys = [{Num, {key, V}} || {["auth","ldap","filters", Num, "key"], V} <- Settings], - Values = [{Num, {value, V}} || {["auth","ldap","filters", Num, "value"], V} <- Settings], - Ops = [{Num, {op, V}} || {["auth","ldap","filters", Num, "op"], V} <- Settings], - RawFilters = Keys ++ Values ++ Ops, - Filters = - lists:foldl( - fun({Num,{T,V}}, Acc)-> - maps:update_with(Num, - fun(F)-> - maps:put(T,V,F) - end, - #{T=>V}, Acc) - end, #{}, RawFilters), - Order=lists:usort(maps:keys(Filters)), - lists:reverse( - lists:foldl( - fun(F,Acc)-> - case F of - #{key:=K, op:=Op, value:=V} -> [Op,{K,V}|Acc]; - #{key:=K, value:=V} -> [{K,V}|Acc] - end - end, - [], - lists:map(fun(K) -> maps:get(K, Filters) end, Order))) -end}. - -{mapping, "auth.ldap.filters.$num.key", "emqx_auth_ldap.filters", [ - {datatype, string} -]}. - -{mapping, "auth.ldap.filters.$num.value", "emqx_auth_ldap.filters", [ - {datatype, string} -]}. - -{mapping, "auth.ldap.filters.$num.op", "emqx_auth_ldap.filters", [ - {datatype, {enum, [ "or", "and" ] } } -]}. - - -{mapping, "auth.ldap.bind_as_user", "emqx_auth_ldap.bind_as_user", [ - {default, false}, - {datatype, {enum, [true, false]}} -]}. - -{mapping, "auth.ldap.username.attributetype", "emqx_auth_ldap.username_attr", [ - {default, "uid"}, - {datatype, string} -]}. - -{mapping, "auth.ldap.password.attributetype", "emqx_auth_ldap.password_attr", [ - {default, "userPassword"}, - {datatype, string} -]}. diff --git a/apps/emqx_auth_ldap/rebar.config b/apps/emqx_auth_ldap/rebar.config deleted file mode 100644 index 811468a7b..000000000 --- a/apps/emqx_auth_ldap/rebar.config +++ /dev/null @@ -1,25 +0,0 @@ -{deps, - [{eldap2, {git, "https://github.com/emqx/eldap2", {tag, "v0.2.2"}}} - ]}. - -{profiles, - [{test, - [{deps, []} - ]} - ]}. - -{edoc_opts, [{preprocess, true}]}. -{erl_opts, [warn_unused_vars, - warn_shadow_vars, - warn_unused_import, - warn_obsolete_guard, - debug_info, - {parse_transform}]}. - -{xref_checks, [undefined_function_calls, undefined_functions, - locals_not_used, deprecated_function_calls, - warnings_as_errors, deprecated_functions]}. -{cover_enabled, true}. -{cover_opts, [verbose]}. -{cover_export_enabled, true}. - diff --git a/apps/emqx_auth_ldap/src/emqx_acl_ldap.erl b/apps/emqx_auth_ldap/src/emqx_acl_ldap.erl deleted file mode 100644 index 25287052c..000000000 --- a/apps/emqx_auth_ldap/src/emqx_acl_ldap.erl +++ /dev/null @@ -1,98 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_acl_ldap). - --include("emqx_auth_ldap.hrl"). - --include_lib("emqx/include/emqx.hrl"). --include_lib("eldap/include/eldap.hrl"). --include_lib("emqx/include/logger.hrl"). - --export([ register_metrics/0 - , check_acl/5 - , description/0 - ]). - --import(proplists, [get_value/2]). - --import(emqx_auth_ldap_cli, [search/4]). - --spec(register_metrics() -> ok). -register_metrics() -> - lists:foreach(fun emqx_metrics:ensure/1, ?ACL_METRICS). - -check_acl(ClientInfo, PubSub, Topic, NoMatchAction, State) -> - case do_check_acl(ClientInfo, PubSub, Topic, NoMatchAction, State) of - ok -> emqx_metrics:inc(?ACL_METRICS(ignore)), ok; - {stop, allow} -> emqx_metrics:inc(?ACL_METRICS(allow)), {stop, allow}; - {stop, deny} -> emqx_metrics:inc(?ACL_METRICS(deny)), {stop, deny} - end. - -do_check_acl(#{username := <<$$, _/binary>>}, _PubSub, _Topic, _NoMatchAction, _State) -> - ok; - -do_check_acl(#{username := Username}, PubSub, Topic, _NoMatchAction, - #{device_dn := DeviceDn, - match_objectclass := ObjectClass, - username_attr := UidAttr, - custom_base_dn := CustomBaseDN, - pool := Pool} = Config) -> - - Filters = maps:get(filters, Config, []), - - ReplaceRules = [{"${username_attr}", UidAttr}, - {"${user}", binary_to_list(Username)}, - {"${device_dn}", DeviceDn}], - - Filter = emqx_auth_ldap:prepare_filter(Filters, UidAttr, ObjectClass, ReplaceRules), - - Attribute = case PubSub of - publish -> "mqttPublishTopic"; - subscribe -> "mqttSubscriptionTopic" - end, - Attribute1 = "mqttPubSubTopic", - ?LOG(debug, "[LDAP] search dn:~p filter:~p, attribute:~p", - [DeviceDn, Filter, Attribute]), - - BaseDN = emqx_auth_ldap:replace_vars(CustomBaseDN, ReplaceRules), - - case search(Pool, BaseDN, Filter, [Attribute, Attribute1]) of - {error, noSuchObject} -> - ok; - {ok, #eldap_search_result{entries = []}} -> - ok; - {ok, #eldap_search_result{entries = [Entry]}} -> - Topics = get_value(Attribute, Entry#eldap_entry.attributes) - ++ get_value(Attribute1, Entry#eldap_entry.attributes), - match(Topic, Topics); - Error -> - ?LOG(error, "[LDAP] search error:~p", [Error]), - {stop, deny} - end. - -match(_Topic, []) -> - ok; - -match(Topic, [Filter | Topics]) -> - case emqx_topic:match(Topic, list_to_binary(Filter)) of - true -> {stop, allow}; - false -> match(Topic, Topics) - end. - -description() -> - "ACL with LDAP". - diff --git a/apps/emqx_auth_ldap/src/emqx_auth_ldap.app.src b/apps/emqx_auth_ldap/src/emqx_auth_ldap.app.src deleted file mode 100644 index 1b76c32c8..000000000 --- a/apps/emqx_auth_ldap/src/emqx_auth_ldap.app.src +++ /dev/null @@ -1,14 +0,0 @@ -{application, emqx_auth_ldap, - [{description, "EMQ X Authentication/ACL with LDAP"}, - {vsn, "4.4.0"}, % strict semver, bump manually! - {modules, []}, - {registered, [emqx_auth_ldap_sup]}, - {applications, [kernel,stdlib,eldap2,ecpool]}, - {mod, {emqx_auth_ldap_app,[]}}, - {env, []}, - {licenses, ["Apache-2.0"]}, - {maintainers, ["EMQ X Team "]}, - {links, [{"Homepage", "https://emqx.io/"}, - {"Github", "https://github.com/emqx/emqx-auth-ldap"} - ]} - ]}. diff --git a/apps/emqx_auth_ldap/src/emqx_auth_ldap.erl b/apps/emqx_auth_ldap/src/emqx_auth_ldap.erl deleted file mode 100644 index 9163362c7..000000000 --- a/apps/emqx_auth_ldap/src/emqx_auth_ldap.erl +++ /dev/null @@ -1,210 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_auth_ldap). - --include("emqx_auth_ldap.hrl"). - --include_lib("emqx/include/emqx.hrl"). --include_lib("eldap/include/eldap.hrl"). --include_lib("emqx/include/logger.hrl"). - --import(proplists, [get_value/2]). - --import(emqx_auth_ldap_cli, [search/3]). - --export([ register_metrics/0 - , check/3 - , description/0 - , prepare_filter/4 - , replace_vars/2 - ]). - --spec(register_metrics() -> ok). -register_metrics() -> - lists:foreach(fun emqx_metrics:ensure/1, ?AUTH_METRICS). - -check(ClientInfo = #{username := Username, password := Password}, AuthResult, - State = #{password_attr := PasswdAttr, bind_as_user := BindAsUserRequired, pool := Pool}) -> - CheckResult = - case lookup_user(Username, State) of - undefined -> {error, not_found}; - {error, Error} -> {error, Error}; - Entry -> - PasswordString = binary_to_list(Password), - ObjectName = Entry#eldap_entry.object_name, - Attributes = Entry#eldap_entry.attributes, - case BindAsUserRequired of - true -> - emqx_auth_ldap_cli:post_bind(Pool, ObjectName, PasswordString); - false -> - case get_value(PasswdAttr, Attributes) of - undefined -> - logger:error("LDAP Search State: ~p, uid: ~p, result:~p", - [State, Username, Attributes]), - {error, not_found}; - [Passhash1] -> - format_password(Passhash1, Password, ClientInfo) - end - end - end, - case CheckResult of - ok -> - ok = emqx_metrics:inc(?AUTH_METRICS(success)), - {stop, AuthResult#{auth_result => success, anonymous => false}}; - {error, not_found} -> - emqx_metrics:inc(?AUTH_METRICS(ignore)); - {error, ResultCode} -> - ok = emqx_metrics:inc(?AUTH_METRICS(failure)), - ?LOG(error, "[LDAP] Auth from ldap failed: ~p", [ResultCode]), - {stop, AuthResult#{auth_result => ResultCode, anonymous => false}} - end. - -lookup_user(Username, #{username_attr := UidAttr, - match_objectclass := ObjectClass, - device_dn := DeviceDn, - custom_base_dn := CustomBaseDN, pool := Pool} = Config) -> - - Filters = maps:get(filters, Config, []), - - ReplaceRules = [{"${username_attr}", UidAttr}, - {"${user}", binary_to_list(Username)}, - {"${device_dn}", DeviceDn}], - - Filter = prepare_filter(Filters, UidAttr, ObjectClass, ReplaceRules), - - %% auth.ldap.custom_base_dn = "${username_attr}=${user},${device_dn}" - BaseDN = replace_vars(CustomBaseDN, ReplaceRules), - - case search(Pool, BaseDN, Filter) of - %% This clause seems to be impossible to match. `eldap2:search/2` does - %% not validates the result, so if it returns "successfully" from the - %% LDAP server, it always returns `{ok, #eldap_search_result{}}`. - {error, noSuchObject} -> - undefined; - %% In case no user was found by the search, but the search was completed - %% without error we get an empty `entries` list. - {ok, #eldap_search_result{entries = []}} -> - undefined; - {ok, #eldap_search_result{entries = [Entry]}} -> - Attributes = Entry#eldap_entry.attributes, - case get_value("isEnabled", Attributes) of - undefined -> - Entry; - [Val] -> - case list_to_atom(string:to_lower(Val)) of - true -> Entry; - false -> {error, username_disabled} - end - end; - {error, Error} -> - ?LOG(error, "[LDAP] Search dn: ~p, filter: ~p, fail:~p", [DeviceDn, Filter, Error]), - {error, username_or_password_error} - end. - -check_pass(Password, Password, _ClientInfo) -> ok; -check_pass(_, _, _) -> {error, bad_username_or_password}. - -format_password(Passhash, Password, ClientInfo) -> - case do_format_password(Passhash, Password) of - {error, Error2} -> - {error, Error2}; - {Passhash1, Password1} -> - check_pass(Passhash1, Password1, ClientInfo) - end. - -do_format_password(Passhash, Password) -> - Base64PasshashHandler = - handle_passhash(fun(HashType, Passhash1, Password1) -> - Passhash2 = binary_to_list(base64:decode(Passhash1)), - resolve_passhash(HashType, Passhash2, Password1) - end, - fun(_Passhash, _Password) -> - {error, password_error} - end), - PasshashHandler = handle_passhash(fun resolve_passhash/3, Base64PasshashHandler), - PasshashHandler(Passhash, Password). - -resolve_passhash(HashType, Passhash, Password) -> - [_, Passhash1] = string:tokens(Passhash, "}"), - do_resolve(HashType, Passhash1, Password). - -handle_passhash(HandleMatch, HandleNoMatch) -> - fun(Passhash, Password) -> - case re:run(Passhash, "(?<={)[^{}]+(?=})", [{capture, all, list}, global]) of - {match, [[HashType]]} -> - HandleMatch(list_to_atom(string:to_lower(HashType)), Passhash, Password); - _ -> - HandleNoMatch(Passhash, Password) - end - end. - -do_resolve(ssha, Passhash, Password) -> - D64 = base64:decode(Passhash), - {HashedData, Salt} = lists:split(20, binary_to_list(D64)), - NewHash = crypto:hash(sha, <>), - {list_to_binary(HashedData), NewHash}; -do_resolve(HashType, Passhash, Password) -> - Password1 = base64:encode(crypto:hash(HashType, Password)), - {list_to_binary(Passhash), Password1}. - -description() -> "LDAP Authentication Plugin". - -prepare_filter(Filters, _UidAttr, ObjectClass, ReplaceRules) -> - SubFilters = - lists:map(fun({K, V}) -> - {replace_vars(K, ReplaceRules), replace_vars(V, ReplaceRules)}; - (Op) -> - Op - end, Filters), - case SubFilters of - [] -> eldap2:equalityMatch("objectClass", ObjectClass); - _List -> compile_filters(SubFilters, []) - end. - - -compile_filters([{Key, Value}], []) -> - compile_equal(Key, Value); -compile_filters([{K1, V1}, "and", {K2, V2} | Rest], []) -> - compile_filters( - Rest, - eldap2:'and'([compile_equal(K1, V1), - compile_equal(K2, V2)])); -compile_filters([{K1, V1}, "or", {K2, V2} | Rest], []) -> - compile_filters( - Rest, - eldap2:'or'([compile_equal(K1, V1), - compile_equal(K2, V2)])); -compile_filters(["and", {K, V} | Rest], PartialFilter) -> - compile_filters( - Rest, - eldap2:'and'([PartialFilter, - compile_equal(K, V)])); -compile_filters(["or", {K, V} | Rest], PartialFilter) -> - compile_filters( - Rest, - eldap2:'or'([PartialFilter, - compile_equal(K, V)])); -compile_filters([], Filter) -> - Filter. - -compile_equal(Key, Value) -> - eldap2:equalityMatch(Key, Value). - -replace_vars(CustomBaseDN, ReplaceRules) -> - lists:foldl(fun({Pattern, Substitute}, DN) -> - lists:flatten(string:replace(DN, Pattern, Substitute)) - end, CustomBaseDN, ReplaceRules). diff --git a/apps/emqx_auth_ldap/src/emqx_auth_ldap_app.erl b/apps/emqx_auth_ldap/src/emqx_auth_ldap_app.erl deleted file mode 100644 index ed43c8d26..000000000 --- a/apps/emqx_auth_ldap/src/emqx_auth_ldap_app.erl +++ /dev/null @@ -1,78 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_auth_ldap_app). - --behaviour(application). - --emqx_plugin(auth). - --include("emqx_auth_ldap.hrl"). - -%% Application callbacks --export([ start/2 - , prep_stop/1 - , stop/1 - ]). - -start(_StartType, _StartArgs) -> - {ok, Sup} = emqx_auth_ldap_sup:start_link(), - _ = if_enabled([device_dn, match_objectclass, - username_attr, password_attr, - filters, custom_base_dn, bind_as_user], - fun load_auth_hook/1), - _ = if_enabled([device_dn, match_objectclass, - username_attr, password_attr, - filters, custom_base_dn, bind_as_user], - fun load_acl_hook/1), - {ok, Sup}. - -prep_stop(State) -> - emqx:unhook('client.authenticate',{emqx_auth_ldap, check}), - emqx:unhook('client.check_acl', {emqx_acl_ldap, check_acl}), - State. - -stop(_State) -> - ok. - -load_auth_hook(DeviceDn) -> - ok = emqx_auth_ldap:register_metrics(), - Params = maps:from_list(DeviceDn), - emqx:hook('client.authenticate', {emqx_auth_ldap, check, [Params#{pool => ?APP}]}). - -load_acl_hook(DeviceDn) -> - ok = emqx_acl_ldap:register_metrics(), - Params = maps:from_list(DeviceDn), - emqx:hook('client.check_acl', {emqx_acl_ldap, check_acl, [Params#{pool => ?APP}]}). - -if_enabled(Cfgs, Fun) -> - case get_env(Cfgs) of - {ok, []} -> ok; - {ok, InitArgs} -> Fun(InitArgs) - end. - -get_env(Cfgs) -> - get_env(Cfgs, []). - -get_env([Cfg | LeftCfgs], ENVS) -> - case application:get_env(?APP, Cfg) of - {ok, ENV} -> - get_env(LeftCfgs, [{Cfg, ENV} | ENVS]); - undefined -> - get_env(LeftCfgs, ENVS) - end; -get_env([], ENVS) -> - {ok, ENVS}. diff --git a/apps/emqx_auth_ldap/src/emqx_auth_ldap_cli.erl b/apps/emqx_auth_ldap/src/emqx_auth_ldap_cli.erl deleted file mode 100644 index 412754664..000000000 --- a/apps/emqx_auth_ldap/src/emqx_auth_ldap_cli.erl +++ /dev/null @@ -1,150 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_auth_ldap_cli). - --behaviour(ecpool_worker). - --include("emqx_auth_ldap.hrl"). - --include_lib("emqx/include/emqx.hrl"). --include_lib("emqx/include/logger.hrl"). - -%% ecpool callback --export([connect/1]). - --export([ search/3 - , search/4 - , post_bind/3 - , init_args/1 - ]). - --import(proplists, - [ get_value/2 - , get_value/3 - ]). - -%%-------------------------------------------------------------------- -%% LDAP Connect/Search -%%-------------------------------------------------------------------- - -connect(Opts) -> - Servers = get_value(servers, Opts, ["localhost"]), - Port = get_value(port, Opts, 389), - Timeout = get_value(timeout, Opts, 30), - BindDn = get_value(bind_dn, Opts), - BindPassword = get_value(bind_password, Opts), - LdapOpts = case get_value(ssl, Opts, false)of - true -> - SslOpts = get_value(sslopts, Opts), - [{port, Port}, {timeout, Timeout}, {sslopts, SslOpts}]; - false -> - [{port, Port}, {timeout, Timeout}] - end, - ?LOG(debug, "[LDAP] Connecting to OpenLDAP server: ~p, Opts:~p ...", [Servers, LdapOpts]), - - case eldap2:open(Servers, LdapOpts) of - {ok, LDAP} -> - try eldap2:simple_bind(LDAP, BindDn, BindPassword) of - ok -> {ok, LDAP}; - {error, Error} -> - ?LOG(error, "[LDAP] Can't authenticated to OpenLDAP server: ~p", [Error]), - {error, Error} - catch - error:Reason -> - ?LOG(error, "[LDAP] Can't authenticated to OpenLDAP server: ~p", [Reason]), - {error, Reason} - end; - {error, Reason} -> - ?LOG(error, "[LDAP] Can't connect to OpenLDAP server: ~p", [Reason]), - {error, Reason} - end. - -search(Pool, Base, Filter) -> - ecpool:with_client(Pool, - fun(C) -> - case application:get_env(?APP, bind_as_user) of - {ok, true} -> - {ok, Opts} = application:get_env(?APP, ldap), - BindDn = get_value(bind_dn, Opts), - BindPassword = get_value(bind_password, Opts), - try eldap2:simple_bind(C, BindDn, BindPassword) of - ok -> - eldap2:search(C, [{base, Base}, - {filter, Filter}, - {deref, eldap2:derefFindingBaseObj()}]); - {error, Error} -> - {error, Error} - catch - error:Reason -> {error, Reason} - end; - {ok, false} -> - eldap2:search(C, [{base, Base}, - {filter, Filter}, - {deref, eldap2:derefFindingBaseObj()}]) - end - end). - -search(Pool, Base, Filter, Attributes) -> - ecpool:with_client(Pool, - fun(C) -> - case application:get_env(?APP, bind_as_user) of - {ok, true} -> - {ok, Opts} = application:get_env(?APP, ldap), - BindDn = get_value(bind_dn, Opts), - BindPassword = get_value(bind_password, Opts), - try eldap2:simple_bind(C, BindDn, BindPassword) of - ok -> - eldap2:search(C, [{base, Base}, - {filter, Filter}, - {attributes, Attributes}, - {deref, eldap2:derefFindingBaseObj()}]); - {error, Error} -> - {error, Error} - catch - error:Reason -> {error, Reason} - end; - {ok, false} -> - eldap2:search(C, [{base, Base}, - {filter, Filter}, - {attributes, Attributes}, - {deref, eldap2:derefFindingBaseObj()}]) - end - end). - -post_bind(Pool, BindDn, BindPassword) -> - ecpool:with_client(Pool, - fun(C) -> - try eldap2:simple_bind(C, BindDn, BindPassword) of - ok -> ok; - {error, Error} -> - {error, Error} - catch - error:Reason -> {error, Reason} - end - end). - - -init_args(ENVS) -> - DeviceDn = get_value(device_dn, ENVS), - ObjectClass = get_value(match_objectclass, ENVS), - UidAttr = get_value(username_attr, ENVS), - PasswdAttr = get_value(password_attr, ENVS), - {ok, #{device_dn => DeviceDn, - match_objectclass => ObjectClass, - username_attr => UidAttr, - password_attr => PasswdAttr}}. - diff --git a/apps/emqx_auth_ldap/src/emqx_auth_ldap_sup.erl b/apps/emqx_auth_ldap/src/emqx_auth_ldap_sup.erl deleted file mode 100644 index 56b3eea9d..000000000 --- a/apps/emqx_auth_ldap/src/emqx_auth_ldap_sup.erl +++ /dev/null @@ -1,35 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_auth_ldap_sup). - --behaviour(supervisor). - --include("emqx_auth_ldap.hrl"). - --export([start_link/0]). - --export([init/1]). - -start_link() -> - supervisor:start_link({local, ?MODULE}, ?MODULE, []). - -init([]) -> - %% LDAP Connection Pool. - {ok, Server} = application:get_env(?APP, ldap), - PoolSpec = ecpool:pool_spec(?APP, ?APP, emqx_auth_ldap_cli, Server), - {ok, {{one_for_one, 10, 100}, [PoolSpec]}}. - diff --git a/apps/emqx_auth_ldap/test/certs/cacert.pem b/apps/emqx_auth_ldap/test/certs/cacert.pem deleted file mode 100644 index 604fd2362..000000000 --- a/apps/emqx_auth_ldap/test/certs/cacert.pem +++ /dev/null @@ -1,20 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDUTCCAjmgAwIBAgIJAPPYCjTmxdt/MA0GCSqGSIb3DQEBCwUAMD8xCzAJBgNV -BAYTAkNOMREwDwYDVQQIDAhoYW5nemhvdTEMMAoGA1UECgwDRU1RMQ8wDQYDVQQD -DAZSb290Q0EwHhcNMjAwNTA4MDgwNjUyWhcNMzAwNTA2MDgwNjUyWjA/MQswCQYD -VQQGEwJDTjERMA8GA1UECAwIaGFuZ3pob3UxDDAKBgNVBAoMA0VNUTEPMA0GA1UE -AwwGUm9vdENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzcgVLex1 -EZ9ON64EX8v+wcSjzOZpiEOsAOuSXOEN3wb8FKUxCdsGrsJYB7a5VM/Jot25Mod2 -juS3OBMg6r85k2TWjdxUoUs+HiUB/pP/ARaaW6VntpAEokpij/przWMPgJnBF3Ur -MjtbLayH9hGmpQrI5c2vmHQ2reRZnSFbY+2b8SXZ+3lZZgz9+BaQYWdQWfaUWEHZ -uDaNiViVO0OT8DRjCuiDp3yYDj3iLWbTA/gDL6Tf5XuHuEwcOQUrd+h0hyIphO8D -tsrsHZ14j4AWYLk1CPA6pq1HIUvEl2rANx2lVUNv+nt64K/Mr3RnVQd9s8bK+TXQ -KGHd2Lv/PALYuwIDAQABo1AwTjAdBgNVHQ4EFgQUGBmW+iDzxctWAWxmhgdlE8Pj -EbQwHwYDVR0jBBgwFoAUGBmW+iDzxctWAWxmhgdlE8PjEbQwDAYDVR0TBAUwAwEB -/zANBgkqhkiG9w0BAQsFAAOCAQEAGbhRUjpIred4cFAFJ7bbYD9hKu/yzWPWkMRa -ErlCKHmuYsYk+5d16JQhJaFy6MGXfLgo3KV2itl0d+OWNH0U9ULXcglTxy6+njo5 -CFqdUBPwN1jxhzo9yteDMKF4+AHIxbvCAJa17qcwUKR5MKNvv09C6pvQDJLzid7y -E2dkgSuggik3oa0427KvctFf8uhOV94RvEDyqvT5+pgNYZ2Yfga9pD/jjpoHEUlo -88IGU8/wJCx3Ds2yc8+oBg/ynxG8f/HmCC1ET6EHHoe2jlo8FpU/SgGtghS1YL30 -IWxNsPrUP+XsZpBJy/mvOhE5QXo6Y35zDqqj8tI7AGmAWu22jg== ------END CERTIFICATE----- diff --git a/apps/emqx_auth_ldap/test/certs/cert.pem b/apps/emqx_auth_ldap/test/certs/cert.pem deleted file mode 100644 index 092390b1d..000000000 --- a/apps/emqx_auth_ldap/test/certs/cert.pem +++ /dev/null @@ -1,19 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDEzCCAfugAwIBAgIBAjANBgkqhkiG9w0BAQsFADA/MQswCQYDVQQGEwJDTjER -MA8GA1UECAwIaGFuZ3pob3UxDDAKBgNVBAoMA0VNUTEPMA0GA1UEAwwGUm9vdENB -MB4XDTIwMDUwODA4MDcwNVoXDTMwMDUwNjA4MDcwNVowPzELMAkGA1UEBhMCQ04x -ETAPBgNVBAgMCGhhbmd6aG91MQwwCgYDVQQKDANFTVExDzANBgNVBAMMBlNlcnZl -cjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALNeWT3pE+QFfiRJzKmn -AMUrWo3K2j/Tm3+Xnl6WLz67/0rcYrJbbKvS3uyRP/stXyXEKw9CepyQ1ViBVFkW -Aoy8qQEOWFDsZc/5UzhXUnb6LXr3qTkFEjNmhj+7uzv/lbBxlUG1NlYzSeOB6/RT -8zH/lhOeKhLnWYPXdXKsa1FL6ij4X8DeDO1kY7fvAGmBn/THh1uTpDizM4YmeI+7 -4dmayA5xXvARte5h4Vu5SIze7iC057N+vymToMk2Jgk+ZZFpyXrnq+yo6RaD3ANc -lrc4FbeUQZ5a5s5Sxgs9a0Y3WMG+7c5VnVXcbjBRz/aq2NtOnQQjikKKQA8GF080 -BQkCAwEAAaMaMBgwCQYDVR0TBAIwADALBgNVHQ8EBAMCBeAwDQYJKoZIhvcNAQEL -BQADggEBAJefnMZpaRDHQSNUIEL3iwGXE9c6PmIsQVE2ustr+CakBp3TZ4l0enLt -iGMfEVFju69cO4oyokWv+hl5eCMkHBf14Kv51vj448jowYnF1zmzn7SEzm5Uzlsa -sqjtAprnLyof69WtLU1j5rYWBuFX86yOTwRAFNjm9fvhAcrEONBsQtqipBWkMROp -iUYMkRqbKcQMdwxov+lHBYKq9zbWRoqLROAn54SRqgQk6c15JdEfgOOjShbsOkIH -UhqcwRkQic7n1zwHVGVDgNIZVgmJ2IdIWBlPEC7oLrRrBD/X1iEEXtKab6p5o22n -KB5mN+iQaE+Oe2cpGKZJiJRdM+IqDDQ= ------END CERTIFICATE----- diff --git a/apps/emqx_auth_ldap/test/certs/client-cert.pem b/apps/emqx_auth_ldap/test/certs/client-cert.pem deleted file mode 100644 index 09d855221..000000000 --- a/apps/emqx_auth_ldap/test/certs/client-cert.pem +++ /dev/null @@ -1,19 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDEzCCAfugAwIBAgIBATANBgkqhkiG9w0BAQsFADA/MQswCQYDVQQGEwJDTjER -MA8GA1UECAwIaGFuZ3pob3UxDDAKBgNVBAoMA0VNUTEPMA0GA1UEAwwGUm9vdENB -MB4XDTIwMDUwODA4MDY1N1oXDTMwMDUwNjA4MDY1N1owPzELMAkGA1UEBhMCQ04x -ETAPBgNVBAgMCGhhbmd6aG91MQwwCgYDVQQKDANFTVExDzANBgNVBAMMBkNsaWVu -dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMy4hoksKcZBDbY680u6 -TS25U51nuB1FBcGMlF9B/t057wPOlxF/OcmbxY5MwepS41JDGPgulE1V7fpsXkiW -1LUimYV/tsqBfymIe0mlY7oORahKji7zKQ2UBIVFhdlvQxunlIDnw6F9popUgyHt -dMhtlgZK8oqRwHxO5dbfoukYd6J/r+etS5q26sgVkf3C6dt0Td7B25H9qW+f7oLV -PbcHYCa+i73u9670nrpXsC+Qc7Mygwa2Kq/jwU+ftyLQnOeW07DuzOwsziC/fQZa -nbxR+8U9FNftgRcC3uP/JMKYUqsiRAuaDokARZxVTV5hUElfpO6z6/NItSDvvh3i -eikCAwEAAaMaMBgwCQYDVR0TBAIwADALBgNVHQ8EBAMCBeAwDQYJKoZIhvcNAQEL -BQADggEBABchYxKo0YMma7g1qDswJXsR5s56Czx/I+B41YcpMBMTrRqpUC0nHtLk -M7/tZp592u/tT8gzEnQjZLKBAhFeZaR3aaKyknLqwiPqJIgg0pgsBGITrAK3Pv4z -5/YvAJJKgTe5UdeTz6U4lvNEux/4juZ4pmqH4qSFJTOzQS7LmgSmNIdd072rwXBd -UzcSHzsJgEMb88u/LDLjj1pQ7AtZ4Tta8JZTvcgBFmjB0QUi6fgkHY6oGat/W4kR -jSRUBlMUbM/drr2PVzRc2dwbFIl3X+ZE6n5Sl3ZwRAC/s92JU6CPMRW02muVu6xl -goraNgPISnrbpR6KjxLZkVembXzjNNc= ------END CERTIFICATE----- diff --git a/apps/emqx_auth_ldap/test/certs/client-key.pem b/apps/emqx_auth_ldap/test/certs/client-key.pem deleted file mode 100644 index 2b3f30cf6..000000000 --- a/apps/emqx_auth_ldap/test/certs/client-key.pem +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEpAIBAAKCAQEAzLiGiSwpxkENtjrzS7pNLblTnWe4HUUFwYyUX0H+3TnvA86X -EX85yZvFjkzB6lLjUkMY+C6UTVXt+mxeSJbUtSKZhX+2yoF/KYh7SaVjug5FqEqO -LvMpDZQEhUWF2W9DG6eUgOfDoX2milSDIe10yG2WBkryipHAfE7l1t+i6Rh3on+v -561LmrbqyBWR/cLp23RN3sHbkf2pb5/ugtU9twdgJr6Lve73rvSeulewL5BzszKD -BrYqr+PBT5+3ItCc55bTsO7M7CzOIL99BlqdvFH7xT0U1+2BFwLe4/8kwphSqyJE -C5oOiQBFnFVNXmFQSV+k7rPr80i1IO++HeJ6KQIDAQABAoIBAGWgvPjfuaU3qizq -uti/FY07USz0zkuJdkANH6LiSjlchzDmn8wJ0pApCjuIE0PV/g9aS8z4opp5q/gD -UBLM/a8mC/xf2EhTXOMrY7i9p/I3H5FZ4ZehEqIw9sWKK9YzC6dw26HabB2BGOnW -5nozPSQ6cp2RGzJ7BIkxSZwPzPnVTgy3OAuPOiJytvK+hGLhsNaT+Y9bNDvplVT2 -ZwYTV8GlHZC+4b2wNROILm0O86v96O+Qd8nn3fXjGHbMsAnONBq10bZS16L4fvkH -5G+W/1PeSXmtZFppdRRDxIW+DWcXK0D48WRliuxcV4eOOxI+a9N2ZJZZiNLQZGwg -w3A8+mECgYEA8HuJFrlRvdoBe2U/EwUtG74dcyy30L4yEBnN5QscXmEEikhaQCfX -Wm6EieMcIB/5I5TQmSw0cmBMeZjSXYoFdoI16/X6yMMuATdxpvhOZGdUGXxhAH+x -xoTUavWZnEqW3fkUU71kT5E2f2i+0zoatFESXHeslJyz85aAYpP92H0CgYEA2e5A -Yozt5eaA1Gyhd8SeptkEU4xPirNUnVQHStpMWUb1kzTNXrPmNWccQ7JpfpG6DcYl -zUF6p6mlzY+zkMiyPQjwEJlhiHM2NlL1QS7td0R8ewgsFoyn8WsBI4RejWrEG9td -EDniuIw+pBFkcWthnTLHwECHdzgquToyTMjrBB0CgYEA28tdGbrZXhcyAZEhHAZA -Gzog+pKlkpEzeonLKIuGKzCrEKRecIK5jrqyQsCjhS0T7ZRnL4g6i0s+umiV5M5w -fcc292pEA1h45L3DD6OlKplSQVTv55/OYS4oY3YEJtf5mfm8vWi9lQeY8sxOlQpn -O+VZTdBHmTC8PGeTAgZXHZUCgYA6Tyv88lYowB7SN2qQgBQu8jvdGtqhcs/99GCr -H3N0I69LPsKAR0QeH8OJPXBKhDUywESXAaEOwS5yrLNP1tMRz5Vj65YUCzeDG3kx -gpvY4IMp7ArX0bSRvJ6mYSFnVxy3k174G3TVCfksrtagHioVBGQ7xUg5ltafjrms -n8l55QKBgQDVzU8tQvBVqY8/1lnw11Vj4fkE/drZHJ5UkdC1eenOfSWhlSLfUJ8j -ds7vEWpRPPoVuPZYeR1y78cyxKe1GBx6Wa2lF5c7xjmiu0xbRnrxYeLolce9/ntp -asClqpnHT8/VJYTD7Kqj0fouTTZf0zkig/y+2XERppd8k+pSKjUCPQ== ------END RSA PRIVATE KEY----- diff --git a/apps/emqx_auth_ldap/test/certs/key.pem b/apps/emqx_auth_ldap/test/certs/key.pem deleted file mode 100644 index 6c338216e..000000000 --- a/apps/emqx_auth_ldap/test/certs/key.pem +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEowIBAAKCAQEAs15ZPekT5AV+JEnMqacAxStajcraP9Obf5eeXpYvPrv/Stxi -sltsq9Le7JE/+y1fJcQrD0J6nJDVWIFUWRYCjLypAQ5YUOxlz/lTOFdSdvotevep -OQUSM2aGP7u7O/+VsHGVQbU2VjNJ44Hr9FPzMf+WE54qEudZg9d1cqxrUUvqKPhf -wN4M7WRjt+8AaYGf9MeHW5OkOLMzhiZ4j7vh2ZrIDnFe8BG17mHhW7lIjN7uILTn -s36/KZOgyTYmCT5lkWnJeuer7KjpFoPcA1yWtzgVt5RBnlrmzlLGCz1rRjdYwb7t -zlWdVdxuMFHP9qrY206dBCOKQopADwYXTzQFCQIDAQABAoIBAQCuvCbr7Pd3lvI/ -n7VFQG+7pHRe1VKwAxDkx2t8cYos7y/QWcm8Ptwqtw58HzPZGWYrgGMCRpzzkRSF -V9g3wP1S5Scu5C6dBu5YIGc157tqNGXB+SpdZddJQ4Nc6yGHXYERllT04ffBGc3N -WG/oYS/1cSteiSIrsDy/91FvGRCi7FPxH3wIgHssY/tw69s1Cfvaq5lr2NTFzxIG -xCvpJKEdSfVfS9I7LYiymVjst3IOR/w76/ZFY9cRa8ZtmQSWWsm0TUpRC1jdcbkm -ZoJptYWlP+gSwx/fpMYftrkJFGOJhHJHQhwxT5X/ajAISeqjjwkWSEJLwnHQd11C -Zy2+29lBAoGBANlEAIK4VxCqyPXNKfoOOi5dS64NfvyH4A1v2+KaHWc7lqaqPN49 -ezfN2n3X+KWx4cviDD914Yc2JQ1vVJjSaHci7yivocDo2OfZDmjBqzaMp/y+rX1R -/f3MmiTqMa468rjaxI9RRZu7vDgpTR+za1+OBCgMzjvAng8dJuN/5gjlAoGBANNY -uYPKtearBmkqdrSV7eTUe49Nhr0XotLaVBH37TCW0Xv9wjO2xmbm5Ga/DCtPIsBb -yPeYwX9FjoasuadUD7hRvbFu6dBa0HGLmkXRJZTcD7MEX2Lhu4BuC72yDLLFd0r+ -Ep9WP7F5iJyagYqIZtz+4uf7gBvUDdmvXz3sGr1VAoGAdXTD6eeKeiI6PlhKBztF -zOb3EQOO0SsLv3fnodu7ZaHbUgLaoTMPuB17r2jgrYM7FKQCBxTNdfGZmmfDjlLB -0xZ5wL8ibU30ZXL8zTlWPElST9sto4B+FYVVF/vcG9sWeUUb2ncPcJ/Po3UAktDG -jYQTTyuNGtSJHpad/YOZctkCgYBtWRaC7bq3of0rJGFOhdQT9SwItN/lrfj8hyHA -OjpqTV4NfPmhsAtu6j96OZaeQc+FHvgXwt06cE6Rt4RG4uNPRluTFgO7XYFDfitP -vCppnoIw6S5BBvHwPP+uIhUX2bsi/dm8vu8tb+gSvo4PkwtFhEr6I9HglBKmcmog -q6waEQKBgHyecFBeM6Ls11Cd64vborwJPAuxIW7HBAFj/BS99oeG4TjBx4Sz2dFd -rzUibJt4ndnHIvCN8JQkjNG14i9hJln+H3mRss8fbZ9vQdqG+2vOWADYSzzsNI55 -RFY7JjluKcVkp/zCDeUxTU3O6sS+v6/3VE11Cob6OYQx3lN5wrZ3 ------END RSA PRIVATE KEY----- diff --git a/apps/emqx_auth_ldap/test/emqx_auth_ldap_SUITE.erl b/apps/emqx_auth_ldap/test/emqx_auth_ldap_SUITE.erl deleted file mode 100644 index 52bed9cf4..000000000 --- a/apps/emqx_auth_ldap/test/emqx_auth_ldap_SUITE.erl +++ /dev/null @@ -1,152 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_auth_ldap_SUITE). - --compile(export_all). --compile(nowarn_export_all). - --include_lib("emqx/include/emqx.hrl"). --include_lib("eunit/include/eunit.hrl"). --include_lib("common_test/include/ct.hrl"). - --define(PID, emqx_auth_ldap). - --define(APP, emqx_auth_ldap). - --define(DeviceDN, "ou=test_device,dc=emqx,dc=io"). - --define(AuthDN, "ou=test_auth,dc=emqx,dc=io"). - -%%-------------------------------------------------------------------- -%% Setups -%%-------------------------------------------------------------------- - -all() -> - [{group, nossl}, {group, ssl}]. - -groups() -> - Cases = emqx_ct:all(?MODULE), - [{nossl, Cases}, {ssl, Cases}]. - -init_per_group(GrpName, Cfg) -> - Fun = fun(App) -> set_special_configs(GrpName, App) end, - emqx_ct_helpers:start_apps([emqx_auth_ldap], Fun), - Cfg. - -end_per_group(_GrpName, _Cfg) -> - emqx_ct_helpers:stop_apps([emqx_auth_ldap]). - -%%-------------------------------------------------------------------- -%% Cases -%%-------------------------------------------------------------------- - -t_check_auth(_) -> - MqttUser1 = #{clientid => <<"mqttuser1">>, - username => <<"mqttuser0001">>, - password => <<"mqttuser0001">>, - zone => external}, - MqttUser2 = #{clientid => <<"mqttuser2">>, - username => <<"mqttuser0002">>, - password => <<"mqttuser0002">>, - zone => external}, - MqttUser3 = #{clientid => <<"mqttuser3">>, - username => <<"mqttuser0003">>, - password => <<"mqttuser0003">>, - zone => external}, - MqttUser4 = #{clientid => <<"mqttuser4">>, - username => <<"mqttuser0004">>, - password => <<"mqttuser0004">>, - zone => external}, - MqttUser5 = #{clientid => <<"mqttuser5">>, - username => <<"mqttuser0005">>, - password => <<"mqttuser0005">>, - zone => external}, - NonExistUser1 = #{clientid => <<"mqttuser6">>, - username => <<"mqttuser0006">>, - password => <<"mqttuser0006">>, - zone => external}, - NonExistUser2 = #{clientid => <<"mqttuser7">>, - username => <<"mqttuser0005">>, - password => <<"mqttuser0006">>, - zone => external}, - ct:log("MqttUser: ~p", [emqx_access_control:authenticate(MqttUser1)]), - ?assertMatch({ok, #{auth_result := success}}, emqx_access_control:authenticate(MqttUser1)), - ?assertMatch({ok, #{auth_result := success}}, emqx_access_control:authenticate(MqttUser2)), - ?assertMatch({ok, #{auth_result := success}}, emqx_access_control:authenticate(MqttUser3)), - ?assertMatch({ok, #{auth_result := success}}, emqx_access_control:authenticate(MqttUser4)), - ?assertMatch({ok, #{auth_result := success}}, emqx_access_control:authenticate(MqttUser5)), - ?assertEqual({error, not_authorized}, emqx_access_control:authenticate(NonExistUser1)), - ?assertEqual({error, bad_username_or_password}, emqx_access_control:authenticate(NonExistUser2)). - -t_check_acl(_) -> - MqttUser = #{clientid => <<"mqttuser1">>, username => <<"mqttuser0001">>, zone => external}, - NoMqttUser = #{clientid => <<"mqttuser2">>, username => <<"mqttuser0007">>, zone => external}, - allow = emqx_access_control:check_acl(MqttUser, publish, <<"mqttuser0001/pub/1">>), - allow = emqx_access_control:check_acl(MqttUser, publish, <<"mqttuser0001/pub/+">>), - allow = emqx_access_control:check_acl(MqttUser, publish, <<"mqttuser0001/pub/#">>), - - allow = emqx_access_control:check_acl(MqttUser, subscribe, <<"mqttuser0001/sub/1">>), - allow = emqx_access_control:check_acl(MqttUser, subscribe, <<"mqttuser0001/sub/+">>), - allow = emqx_access_control:check_acl(MqttUser, subscribe, <<"mqttuser0001/sub/#">>), - - allow = emqx_access_control:check_acl(MqttUser, publish, <<"mqttuser0001/pubsub/1">>), - allow = emqx_access_control:check_acl(MqttUser, publish, <<"mqttuser0001/pubsub/+">>), - allow = emqx_access_control:check_acl(MqttUser, publish, <<"mqttuser0001/pubsub/#">>), - allow = emqx_access_control:check_acl(MqttUser, subscribe, <<"mqttuser0001/pubsub/1">>), - allow = emqx_access_control:check_acl(MqttUser, subscribe, <<"mqttuser0001/pubsub/+">>), - allow = emqx_access_control:check_acl(MqttUser, subscribe, <<"mqttuser0001/pubsub/#">>), - - deny = emqx_access_control:check_acl(NoMqttUser, publish, <<"mqttuser0001/req/mqttuser0001/+">>), - deny = emqx_access_control:check_acl(MqttUser, publish, <<"mqttuser0001/req/mqttuser0002/+">>), - deny = emqx_access_control:check_acl(MqttUser, subscribe, <<"mqttuser0001/req/+/mqttuser0002">>), - ok. - -%%-------------------------------------------------------------------- -%% Helpers -%%-------------------------------------------------------------------- - -set_special_configs(_, emqx) -> - application:set_env(emqx, allow_anonymous, false), - application:set_env(emqx, enable_acl_cache, false), - application:set_env(emqx, acl_nomatch, deny), - AclFilePath = filename:join(["test", "emqx_SUITE_data", "acl.conf"]), - application:set_env(emqx, acl_file, - emqx_ct_helpers:deps_path(emqx, AclFilePath)), - LoadedPluginPath = filename:join(["test", "emqx_SUITE_data", "loaded_plugins"]), - application:set_env(emqx, plugins_loaded_file, - emqx_ct_helpers:deps_path(emqx, LoadedPluginPath)); - -set_special_configs(Ssl, emqx_auth_ldap) -> - case Ssl == ssl of - true -> - LdapOpts = application:get_env(emqx_auth_ldap, ldap, []), - Path = emqx_ct_helpers:deps_path(emqx_auth_ldap, "test/certs/"), - SslOpts = [{verify, verify_peer}, - {fail_if_no_peer_cert, true}, - {server_name_indication, disable}, - {keyfile, Path ++ "/client-key.pem"}, - {certfile, Path ++ "/client-cert.pem"}, - {cacertfile, Path ++ "/cacert.pem"}], - LdapOpts1 = lists:keystore(ssl, 1, LdapOpts, {ssl, true}), - LdapOpts2 = lists:keystore(sslopts, 1, LdapOpts1, {sslopts, SslOpts}), - LdapOpts3 = lists:keystore(port, 1, LdapOpts2, {port, 636}), - application:set_env(emqx_auth_ldap, ldap, LdapOpts3); - _ -> - ok - end, - application:set_env(emqx_auth_ldap, device_dn, "ou=testdevice, dc=emqx, dc=io"). - diff --git a/apps/emqx_auth_ldap/test/emqx_auth_ldap_bind_as_user_SUITE.erl b/apps/emqx_auth_ldap/test/emqx_auth_ldap_bind_as_user_SUITE.erl deleted file mode 100644 index 24c03fdaf..000000000 --- a/apps/emqx_auth_ldap/test/emqx_auth_ldap_bind_as_user_SUITE.erl +++ /dev/null @@ -1,112 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_auth_ldap_bind_as_user_SUITE). - --compile(nowarn_export_all). --compile(export_all). - --include_lib("emqx/include/emqx.hrl"). --include_lib("eunit/include/eunit.hrl"). --include_lib("common_test/include/ct.hrl"). - --define(PID, emqx_auth_ldap). - --define(APP, emqx_auth_ldap). - --define(DeviceDN, "ou=test_device,dc=emqx,dc=io"). - --define(AuthDN, "ou=test_auth,dc=emqx,dc=io"). - -all() -> - [check_auth, - check_acl]. - -init_per_suite(Config) -> - emqx_ct_helpers:start_apps([emqx_auth_ldap], fun set_special_configs/1), - Config. - -end_per_suite(_Config) -> - emqx_ct_helpers:stop_apps([emqx_auth_ldap]). - -check_auth(_) -> - MqttUser1 = #{clientid => <<"mqttuser1">>, - username => <<"user1">>, - password => <<"mqttuser0001">>, - zone => external}, - MqttUser2 = #{clientid => <<"mqttuser2">>, - username => <<"user2">>, - password => <<"mqttuser0002">>, - zone => external}, - NonExistUser1 = #{clientid => <<"mqttuser3">>, - username => <<"user3">>, - password => <<"mqttuser0003">>, - zone => external}, - ct:log("MqttUser: ~p", [emqx_access_control:authenticate(MqttUser1)]), - ?assertMatch({ok, #{auth_result := success}}, emqx_access_control:authenticate(MqttUser1)), - ?assertMatch({ok, #{auth_result := success}}, emqx_access_control:authenticate(MqttUser2)), - ?assertEqual({error, not_authorized}, emqx_access_control:authenticate(NonExistUser1)). - -check_acl(_) -> - MqttUser = #{clientid => <<"mqttuser1">>, username => <<"user1">>, zone => external}, - NoMqttUser = #{clientid => <<"mqttuser2">>, username => <<"user7">>, zone => external}, - allow = emqx_access_control:check_acl(MqttUser, publish, <<"mqttuser0001/pub/1">>), - allow = emqx_access_control:check_acl(MqttUser, publish, <<"mqttuser0001/pub/+">>), - allow = emqx_access_control:check_acl(MqttUser, publish, <<"mqttuser0001/pub/#">>), - - allow = emqx_access_control:check_acl(MqttUser, subscribe, <<"mqttuser0001/sub/1">>), - allow = emqx_access_control:check_acl(MqttUser, subscribe, <<"mqttuser0001/sub/+">>), - allow = emqx_access_control:check_acl(MqttUser, subscribe, <<"mqttuser0001/sub/#">>), - - allow = emqx_access_control:check_acl(MqttUser, publish, <<"mqttuser0001/pubsub/1">>), - allow = emqx_access_control:check_acl(MqttUser, publish, <<"mqttuser0001/pubsub/+">>), - allow = emqx_access_control:check_acl(MqttUser, publish, <<"mqttuser0001/pubsub/#">>), - allow = emqx_access_control:check_acl(MqttUser, subscribe, <<"mqttuser0001/pubsub/1">>), - allow = emqx_access_control:check_acl(MqttUser, subscribe, <<"mqttuser0001/pubsub/+">>), - allow = emqx_access_control:check_acl(MqttUser, subscribe, <<"mqttuser0001/pubsub/#">>), - - deny = emqx_access_control:check_acl(NoMqttUser, publish, <<"mqttuser0001/req/mqttuser0001/+">>), - deny = emqx_access_control:check_acl(MqttUser, publish, <<"mqttuser0001/req/mqttuser0002/+">>), - deny = emqx_access_control:check_acl(MqttUser, subscribe, <<"mqttuser0001/req/+/mqttuser0002">>), - ok. - -set_special_configs(emqx) -> - application:set_env(emqx, allow_anonymous, false), - application:set_env(emqx, enable_acl_cache, false), - application:set_env(emqx, acl_nomatch, deny), - AclFilePath = filename:join(["test", "emqx_SUITE_data", "acl.conf"]), - application:set_env(emqx, acl_file, - emqx_ct_helpers:deps_path(emqx, AclFilePath)), - LoadedPluginPath = filename:join(["test", "emqx_SUITE_data", "loaded_plugins"]), - application:set_env(emqx, plugins_loaded_file, - emqx_ct_helpers:deps_path(emqx, LoadedPluginPath)); - -set_special_configs(emqx_auth_ldap) -> - application:set_env(emqx_auth_ldap, bind_as_user, true), - application:set_env(emqx_auth_ldap, device_dn, "ou=testdevice, dc=emqx, dc=io"), - application:set_env(emqx_auth_ldap, custom_base_dn, "${device_dn}"), - %% auth.ldap.filters.1.key = mqttAccountName - %% auth.ldap.filters.1.value = ${user} - %% auth.ldap.filters.1.op = and - %% auth.ldap.filters.2.key = objectClass - %% auth.ldap.filters.1.value = mqttUser - application:set_env(emqx_auth_ldap, filters, [{"mqttAccountName", "${user}"}, - "and", - {"objectClass", "mqttUser"}]); - -set_special_configs(_App) -> - ok. - diff --git a/apps/emqx_auth_mnesia/.gitignore b/apps/emqx_auth_mnesia/.gitignore deleted file mode 100644 index a4d9fea0a..000000000 --- a/apps/emqx_auth_mnesia/.gitignore +++ /dev/null @@ -1,26 +0,0 @@ -.eunit -deps -*.o -*.beam -*.plt -erl_crash.dump -ebin -rel/example_project -.concrete/DEV_MODE -.rebar -.erlang.mk/ -emqx_auth_mnesia.d -data/ -_build/ -.DS_Store -cover/ -ct.coverdata -eunit.coverdata -logs/ -test/ct.cover.spec -rebar.lock -rebar3.crashdump -erlang.mk -.*.swp -.rebar3/ -etc/emqx_auth_mnesia.conf.rendered diff --git a/apps/emqx_auth_mnesia/README.md b/apps/emqx_auth_mnesia/README.md deleted file mode 100644 index 8b4c145a8..000000000 --- a/apps/emqx_auth_mnesia/README.md +++ /dev/null @@ -1,2 +0,0 @@ -emqx_auth_mnesia -=============== diff --git a/apps/emqx_auth_mnesia/etc/emqx_auth_mnesia.conf b/apps/emqx_auth_mnesia/etc/emqx_auth_mnesia.conf deleted file mode 100644 index 758df1a9c..000000000 --- a/apps/emqx_auth_mnesia/etc/emqx_auth_mnesia.conf +++ /dev/null @@ -1,30 +0,0 @@ -## Password hash. -## -## Value: plain | md5 | sha | sha256 | sha512 -auth.mnesia.password_hash = sha256 - -##-------------------------------------------------------------------- -## ClientId Authentication -##-------------------------------------------------------------------- - -## Examples -##auth.client.1.clientid = id -##auth.client.1.password = passwd -##auth.client.2.clientid = "dev:devid" -##auth.client.2.password = passwd2 -##auth.client.3.clientid = "app:appid" -##auth.client.3.password = passwd3 -##auth.client.4.clientid = "client~!@#$%^&*()_+" -##auth.client.4.password = "passwd~!@#$%^&*()_+" - -##-------------------------------------------------------------------- -## Username Authentication -##-------------------------------------------------------------------- - -## Examples: -##auth.user.1.username = admin -##auth.user.1.password = public -##auth.user.2.username = feng@emqtt.io -##auth.user.2.password = public -##auth.user.3.username = "name~!@#$%^&*()_+" -##auth.user.3.password = "pwsswd~!@#$%^&*()_+" diff --git a/apps/emqx_auth_mnesia/include/emqx_auth_mnesia.hrl b/apps/emqx_auth_mnesia/include/emqx_auth_mnesia.hrl deleted file mode 100644 index 034bd4f30..000000000 --- a/apps/emqx_auth_mnesia/include/emqx_auth_mnesia.hrl +++ /dev/null @@ -1,38 +0,0 @@ --define(APP, emqx_auth_mnesia). - --type(login():: {clientid, binary()} - | {username, binary()}). - --record(emqx_user, { - login :: login(), - password :: binary(), - created_at :: integer() - }). - --record(emqx_acl, { - filter:: {login() | all, emqx_topic:topic()}, - action :: pub | sub | pubsub, - access :: allow | deny, - created_at :: integer() - }). - --record(auth_metrics, { - success = 'client.auth.success', - failure = 'client.auth.failure', - ignore = 'client.auth.ignore' - }). - --record(acl_metrics, { - allow = 'client.acl.allow', - deny = 'client.acl.deny', - ignore = 'client.acl.ignore' - }). - --define(METRICS(Type), tl(tuple_to_list(#Type{}))). --define(METRICS(Type, K), #Type{}#Type.K). - --define(AUTH_METRICS, ?METRICS(auth_metrics)). --define(AUTH_METRICS(K), ?METRICS(auth_metrics, K)). - --define(ACL_METRICS, ?METRICS(acl_metrics)). --define(ACL_METRICS(K), ?METRICS(acl_metrics, K)). diff --git a/apps/emqx_auth_mnesia/priv/emqx_auth_mnesia.schema b/apps/emqx_auth_mnesia/priv/emqx_auth_mnesia.schema deleted file mode 100644 index 87d6bf47f..000000000 --- a/apps/emqx_auth_mnesia/priv/emqx_auth_mnesia.schema +++ /dev/null @@ -1,43 +0,0 @@ -%%-*- mode: erlang -*- -%% emqx_auth_mnesia config mapping - -{mapping, "auth.mnesia.password_hash", "emqx_auth_mnesia.password_hash", [ - {default, sha256}, - {datatype, {enum, [plain, md5, sha, sha256, sha512]}} -]}. - -{mapping, "auth.client.$id.clientid", "emqx_auth_mnesia.clientid_list", [ - {datatype, string} -]}. - -{mapping, "auth.client.$id.password", "emqx_auth_mnesia.clientid_list", [ - {datatype, string} -]}. - -{translation, "emqx_auth_mnesia.clientid_list", fun(Conf) -> - ClientList = cuttlefish_variable:filter_by_prefix("auth.client", Conf), - lists:foldl( - fun({["auth", "client", Id, "clientid"], ClientId}, AccIn) -> - [{ClientId, cuttlefish:conf_get("auth.client." ++ Id ++ ".password", Conf)} | AccIn]; - (_, AccIn) -> - AccIn - end, [], ClientList) -end}. - -{mapping, "auth.user.$id.username", "emqx_auth_mnesia.username_list", [ - {datatype, string} -]}. - -{mapping, "auth.user.$id.password", "emqx_auth_mnesia.username_list", [ - {datatype, string} -]}. - -{translation, "emqx_auth_mnesia.username_list", fun(Conf) -> - Userlist = cuttlefish_variable:filter_by_prefix("auth.user", Conf), - lists:foldl( - fun({["auth", "user", Id, "username"], Username}, AccIn) -> - [{Username, cuttlefish:conf_get("auth.user." ++ Id ++ ".password", Conf)} | AccIn]; - (_, AccIn) -> - AccIn - end, [], Userlist) -end}. diff --git a/apps/emqx_auth_mnesia/rebar.config b/apps/emqx_auth_mnesia/rebar.config deleted file mode 100644 index 4c695ec69..000000000 --- a/apps/emqx_auth_mnesia/rebar.config +++ /dev/null @@ -1,17 +0,0 @@ -{deps, - [ ]}. - -{erl_opts, [warn_unused_vars, - warn_shadow_vars, - warn_unused_import, - warn_obsolete_guard, - debug_info, - {parse_transform}]}. - -{xref_checks, [undefined_function_calls, undefined_functions, - locals_not_used, deprecated_function_calls, - warnings_as_errors, deprecated_functions]}. -{cover_enabled, true}. -{cover_opts, [verbose]}. -{cover_export_enabled, true}. - diff --git a/apps/emqx_auth_mnesia/src/emqx_acl_mnesia.erl b/apps/emqx_auth_mnesia/src/emqx_acl_mnesia.erl deleted file mode 100644 index c21955182..000000000 --- a/apps/emqx_auth_mnesia/src/emqx_acl_mnesia.erl +++ /dev/null @@ -1,104 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_acl_mnesia). - --include("emqx_auth_mnesia.hrl"). - --include_lib("stdlib/include/ms_transform.hrl"). - --define(TABLE, emqx_acl). - -%% ACL Callbacks --export([ init/0 - , register_metrics/0 - , check_acl/5 - , description/0 - ]). - -init() -> - ok = ekka_mnesia:create_table(emqx_acl, [ - {type, bag}, - {disc_copies, [node()]}, - {attributes, record_info(fields, emqx_acl)}, - {storage_properties, [{ets, [{read_concurrency, true}]}]}]), - ok = ekka_mnesia:copy_table(emqx_acl, disc_copies). - --spec(register_metrics() -> ok). -register_metrics() -> - lists:foreach(fun emqx_metrics:ensure/1, ?ACL_METRICS). - -check_acl(ClientInfo = #{ clientid := Clientid }, PubSub, Topic, _NoMatchAction, _Params) -> - Username = maps:get(username, ClientInfo, undefined), - - Acls = case Username of - undefined -> - emqx_acl_mnesia_cli:lookup_acl({clientid, Clientid}) ++ - emqx_acl_mnesia_cli:lookup_acl(all); - _ -> - emqx_acl_mnesia_cli:lookup_acl({clientid, Clientid}) ++ - emqx_acl_mnesia_cli:lookup_acl({username, Username}) ++ - emqx_acl_mnesia_cli:lookup_acl(all) - end, - - case match(ClientInfo, PubSub, Topic, Acls) of - allow -> - emqx_metrics:inc(?ACL_METRICS(allow)), - {stop, allow}; - deny -> - emqx_metrics:inc(?ACL_METRICS(deny)), - {stop, deny}; - _ -> - emqx_metrics:inc(?ACL_METRICS(ignore)), - ok - end. - -description() -> "Acl with Mnesia". - -%%-------------------------------------------------------------------- -%% Internal functions -%%------------------------------------------------------------------- - -match(_ClientInfo, _PubSub, _Topic, []) -> - nomatch; -match(ClientInfo, PubSub, Topic, [ {_, ACLTopic, Action, Access, _} | Acls]) -> - case match_actions(PubSub, Action) andalso match_topic(ClientInfo, Topic, ACLTopic) of - true -> Access; - false -> match(ClientInfo, PubSub, Topic, Acls) - end. - -match_topic(ClientInfo, Topic, ACLTopic) when is_binary(Topic) -> - emqx_topic:match(Topic, feed_var(ClientInfo, ACLTopic)). - -match_actions(_, pubsub) -> true; -match_actions(subscribe, sub) -> true; -match_actions(publish, pub) -> true; -match_actions(_, _) -> false. - -feed_var(ClientInfo, Pattern) -> - feed_var(ClientInfo, emqx_topic:words(Pattern), []). -feed_var(_ClientInfo, [], Acc) -> - emqx_topic:join(lists:reverse(Acc)); -feed_var(ClientInfo = #{clientid := undefined}, [<<"%c">>|Words], Acc) -> - feed_var(ClientInfo, Words, [<<"%c">>|Acc]); -feed_var(ClientInfo = #{clientid := ClientId}, [<<"%c">>|Words], Acc) -> - feed_var(ClientInfo, Words, [ClientId |Acc]); -feed_var(ClientInfo = #{username := undefined}, [<<"%u">>|Words], Acc) -> - feed_var(ClientInfo, Words, [<<"%u">>|Acc]); -feed_var(ClientInfo = #{username := Username}, [<<"%u">>|Words], Acc) -> - feed_var(ClientInfo, Words, [Username|Acc]); -feed_var(ClientInfo, [W|Words], Acc) -> - feed_var(ClientInfo, Words, [W|Acc]). diff --git a/apps/emqx_auth_mnesia/src/emqx_acl_mnesia_api.erl b/apps/emqx_auth_mnesia/src/emqx_acl_mnesia_api.erl deleted file mode 100644 index 63e8fedd1..000000000 --- a/apps/emqx_auth_mnesia/src/emqx_acl_mnesia_api.erl +++ /dev/null @@ -1,226 +0,0 @@ -%c%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_acl_mnesia_api). - --include("emqx_auth_mnesia.hrl"). - --include_lib("stdlib/include/ms_transform.hrl"). - --import(proplists, [ get_value/2 - , get_value/3 - ]). - --import(minirest, [return/1]). - --rest_api(#{name => list_clientid, - method => 'GET', - path => "/acl/clientid", - func => list_clientid, - descr => "List available mnesia in the cluster" - }). - --rest_api(#{name => list_username, - method => 'GET', - path => "/acl/username", - func => list_username, - descr => "List available mnesia in the cluster" - }). - --rest_api(#{name => list_all, - method => 'GET', - path => "/acl/$all", - func => list_all, - descr => "List available mnesia in the cluster" - }). - --rest_api(#{name => lookup_clientid, - method => 'GET', - path => "/acl/clientid/:bin:clientid", - func => lookup, - descr => "Lookup mnesia in the cluster" - }). - --rest_api(#{name => lookup_username, - method => 'GET', - path => "/acl/username/:bin:username", - func => lookup, - descr => "Lookup mnesia in the cluster" - }). - --rest_api(#{name => add, - method => 'POST', - path => "/acl", - func => add, - descr => "Add mnesia in the cluster" - }). - --rest_api(#{name => delete_clientid, - method => 'DELETE', - path => "/acl/clientid/:bin:clientid/topic/:bin:topic", - func => delete, - descr => "Delete mnesia in the cluster" - }). - --rest_api(#{name => delete_username, - method => 'DELETE', - path => "/acl/username/:bin:username/topic/:bin:topic", - func => delete, - descr => "Delete mnesia in the cluster" - }). - --rest_api(#{name => delete_all, - method => 'DELETE', - path => "/acl/$all/topic/:bin:topic", - func => delete, - descr => "Delete mnesia in the cluster" - }). - - --export([ list_clientid/2 - , list_username/2 - , list_all/2 - , lookup/2 - , add/2 - , delete/2 - ]). - -list_clientid(_Bindings, Params) -> - MatchSpec = ets:fun2ms( - fun({emqx_acl, {{clientid, Clientid}, Topic}, Action, Access, CreatedAt}) -> {{clientid,Clientid}, Topic, Action,Access, CreatedAt} end), - return({ok, emqx_auth_mnesia_api:paginate(emqx_acl, MatchSpec, Params, fun emqx_acl_mnesia_cli:comparing/2, fun format/1)}). - -list_username(_Bindings, Params) -> - MatchSpec = ets:fun2ms( - fun({emqx_acl, {{username, Username}, Topic}, Action, Access, CreatedAt}) -> {{username, Username}, Topic, Action,Access, CreatedAt} end), - return({ok, emqx_auth_mnesia_api:paginate(emqx_acl, MatchSpec, Params, fun emqx_acl_mnesia_cli:comparing/2, fun format/1)}). - -list_all(_Bindings, Params) -> - MatchSpec = ets:fun2ms( - fun({emqx_acl, {all, Topic}, Action, Access, CreatedAt}) -> {all, Topic, Action,Access, CreatedAt}end - ), - return({ok, emqx_auth_mnesia_api:paginate(emqx_acl, MatchSpec, Params, fun emqx_acl_mnesia_cli:comparing/2, fun format/1)}). - - -lookup(#{clientid := Clientid}, _Params) -> - return({ok, format(emqx_acl_mnesia_cli:lookup_acl({clientid, urldecode(Clientid)}))}); -lookup(#{username := Username}, _Params) -> - return({ok, format(emqx_acl_mnesia_cli:lookup_acl({username, urldecode(Username)}))}). - -add(_Bindings, Params) -> - [ P | _] = Params, - case is_list(P) of - true -> return(do_add(Params, [])); - false -> - Re = do_add(Params), - case Re of - #{result := ok} -> return({ok, Re}); - #{result := <<"ok">>} -> return({ok, Re}); - _ -> return({error, {add, Re}}) - end - end. - -do_add([ Params | ParamsN ], ReList) -> - do_add(ParamsN, [do_add(Params) | ReList]); - -do_add([], ReList) -> - {ok, ReList}. - -do_add(Params) -> - Clientid = get_value(<<"clientid">>, Params, undefined), - Username = get_value(<<"username">>, Params, undefined), - Login = case {Clientid, Username} of - {undefined, undefined} -> all; - {_, undefined} -> {clientid, urldecode(Clientid)}; - {undefined, _} -> {username, urldecode(Username)} - end, - Topic = urldecode(get_value(<<"topic">>, Params)), - Action = urldecode(get_value(<<"action">>, Params)), - Access = urldecode(get_value(<<"access">>, Params)), - Re = case validate([login, topic, action, access], [Login, Topic, Action, Access]) of - ok -> - emqx_acl_mnesia_cli:add_acl(Login, Topic, erlang:binary_to_atom(Action, utf8), erlang:binary_to_atom(Access, utf8)); - Err -> Err - end, - maps:merge(#{topic => Topic, - action => Action, - access => Access, - result => format_msg(Re) - }, case Login of - all -> #{all => '$all'}; - _ -> maps:from_list([Login]) - end). - -delete(#{clientid := Clientid, topic := Topic}, _) -> - return(emqx_acl_mnesia_cli:remove_acl({clientid, urldecode(Clientid)}, urldecode(Topic))); -delete(#{username := Username, topic := Topic}, _) -> - return(emqx_acl_mnesia_cli:remove_acl({username, urldecode(Username)}, urldecode(Topic))); -delete(#{topic := Topic}, _) -> - return(emqx_acl_mnesia_cli:remove_acl(all, urldecode(Topic))). - -%%------------------------------------------------------------------------------ -%% Interval Funcs -%%------------------------------------------------------------------------------ -format({{clientid, Clientid}, Topic, Action, Access, _CreatedAt}) -> - #{clientid => Clientid, topic => Topic, action => Action, access => Access}; -format({{username, Username}, Topic, Action, Access, _CreatedAt}) -> - #{username => Username, topic => Topic, action => Action, access => Access}; -format({all, Topic, Action, Access, _CreatedAt}) -> - #{all => '$all', topic => Topic, action => Action, access => Access}; -format(List) when is_list(List) -> - format(List, []). - -format([L | List], Relist) -> - format(List, [format(L) | Relist]); -format([], ReList) -> lists:reverse(ReList). - -validate([], []) -> - ok; -validate([K|Keys], [V|Values]) -> - case do_validation(K, V) of - false -> {error, K}; - true -> validate(Keys, Values) - end. -do_validation(login, all) -> - true; -do_validation(login, {clientid, V}) when is_binary(V) - andalso byte_size(V) > 0 -> - true; -do_validation(login, {username, V}) when is_binary(V) - andalso byte_size(V) > 0 -> - true; -do_validation(topic, V) when is_binary(V) - andalso byte_size(V) > 0 -> - true; -do_validation(action, V) when is_binary(V) -> - case V =:= <<"pub">> orelse V =:= <<"sub">> orelse V =:= <<"pubsub">> of - true -> true; - false -> false - end; -do_validation(access, V) when V =:= <<"allow">> orelse V =:= <<"deny">> -> - true; -do_validation(_, _) -> - false. - -format_msg(Message) - when is_atom(Message); - is_binary(Message) -> Message; - -format_msg(Message) when is_tuple(Message) -> - iolist_to_binary(io_lib:format("~p", [Message])). - -urldecode(S) -> - emqx_http_lib:uri_decode(S). diff --git a/apps/emqx_auth_mnesia/src/emqx_acl_mnesia_cli.erl b/apps/emqx_auth_mnesia/src/emqx_acl_mnesia_cli.erl deleted file mode 100644 index 302a81637..000000000 --- a/apps/emqx_auth_mnesia/src/emqx_acl_mnesia_cli.erl +++ /dev/null @@ -1,270 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_acl_mnesia_cli). - --include("emqx_auth_mnesia.hrl"). --include_lib("emqx/include/logger.hrl"). --include_lib("stdlib/include/ms_transform.hrl"). --define(TABLE, emqx_acl). - -%% Acl APIs --export([ add_acl/4 - , lookup_acl/1 - , all_acls/0 - , all_acls/1 - , remove_acl/2 - ]). - --export([cli/1]). --export([comparing/2]). -%%-------------------------------------------------------------------- -%% Acl API -%%-------------------------------------------------------------------- - -%% @doc Add Acls --spec(add_acl(login() | all, emqx_topic:topic(), pub | sub | pubsub, allow | deny) -> - ok | {error, any()}). -add_acl(Login, Topic, Action, Access) -> - Filter = {Login, Topic}, - Acl = #?TABLE{ - filter = Filter, - action = Action, - access = Access, - created_at = erlang:system_time(millisecond) - }, - ret(mnesia:transaction( - fun() -> - OldRecords = mnesia:wread({?TABLE, Filter}), - case Action of - pubsub -> - update_permission(pub, Acl, OldRecords), - update_permission(sub, Acl, OldRecords); - _ -> - update_permission(Action, Acl, OldRecords) - end - end)). - -%% @doc Lookup acl by login --spec(lookup_acl(login() | all) -> list()). -lookup_acl(undefined) -> []; -lookup_acl(Login) -> - MatchSpec = ets:fun2ms(fun({?TABLE, {Filter, ACLTopic}, Action, Access, CreatedAt}) - when Filter =:= Login -> - {Filter, ACLTopic, Action, Access, CreatedAt} - end), - lists:sort(fun comparing/2, ets:select(?TABLE, MatchSpec)). - -%% @doc Remove acl --spec(remove_acl(login() | all, emqx_topic:topic()) -> ok | {error, any()}). -remove_acl(Login, Topic) -> - ret(mnesia:transaction(fun mnesia:delete/1, [{?TABLE, {Login, Topic}}])). - -%% @doc All logins --spec(all_acls() -> list()). -all_acls() -> - all_acls(clientid) ++ - all_acls(username) ++ - all_acls(all). - -all_acls(clientid) -> - MatchSpec = ets:fun2ms( - fun({?TABLE, {{clientid, Clientid}, Topic}, Action, Access, CreatedAt}) -> - {{clientid, Clientid}, Topic, Action, Access, CreatedAt} - end), - lists:sort(fun comparing/2, ets:select(?TABLE, MatchSpec)); -all_acls(username) -> - MatchSpec = ets:fun2ms( - fun({?TABLE, {{username, Username}, Topic}, Action, Access, CreatedAt}) -> - {{username, Username}, Topic, Action, Access, CreatedAt} - end), - lists:sort(fun comparing/2, ets:select(?TABLE, MatchSpec)); -all_acls(all) -> - MatchSpec = ets:fun2ms( - fun({?TABLE, {all, Topic}, Action, Access, CreatedAt}) -> - {all, Topic, Action, Access, CreatedAt} - end - ), - lists:sort(fun comparing/2, ets:select(?TABLE, MatchSpec)). - -%%-------------------------------------------------------------------- -%% ACL Cli -%%-------------------------------------------------------------------- - -cli(["list"]) -> - [print_acl(Acl) || Acl <- all_acls()]; - -cli(["list", "clientid"]) -> - [print_acl(Acl) || Acl <- all_acls(clientid)]; - -cli(["list", "username"]) -> - [print_acl(Acl) || Acl <- all_acls(username)]; - -cli(["list", "_all"]) -> - [print_acl(Acl) || Acl <- all_acls(all)]; - -cli(["add", "clientid", Clientid, Topic, Action, Access]) -> - case validate(action, Action) andalso validate(access, Access) of - true -> - case add_acl( - {clientid, iolist_to_binary(Clientid)}, - iolist_to_binary(Topic), - list_to_existing_atom(Action), - list_to_existing_atom(Access) - ) of - ok -> emqx_ctl:print("ok~n"); - {error, Reason} -> emqx_ctl:print("Error: ~p~n", [Reason]) - end; - _ -> - emqx_ctl:print("Error: Input is illegal~n") - end; - -cli(["add", "username", Username, Topic, Action, Access]) -> - case validate(action, Action) andalso validate(access, Access) of - true -> - case add_acl( - {username, iolist_to_binary(Username)}, - iolist_to_binary(Topic), - list_to_existing_atom(Action), - list_to_existing_atom(Access) - ) of - ok -> emqx_ctl:print("ok~n"); - {error, Reason} -> emqx_ctl:print("Error: ~p~n", [Reason]) - end; - _ -> - emqx_ctl:print("Error: Input is illegal~n") - end; - -cli(["add", "_all", Topic, Action, Access]) -> - case validate(action, Action) andalso validate(access, Access) of - true -> - case add_acl( - all, - iolist_to_binary(Topic), - list_to_existing_atom(Action), - list_to_existing_atom(Access) - ) of - ok -> emqx_ctl:print("ok~n"); - {error, Reason} -> emqx_ctl:print("Error: ~p~n", [Reason]) - end; - _ -> - emqx_ctl:print("Error: Input is illegal~n") - end; - -cli(["show", "clientid", Clientid]) -> - [print_acl(Acl) || Acl <- lookup_acl({clientid, iolist_to_binary(Clientid)})]; - -cli(["show", "username", Username]) -> - [print_acl(Acl) || Acl <- lookup_acl({username, iolist_to_binary(Username)})]; - -cli(["del", "clientid", Clientid, Topic])-> - cli(["delete", "clientid", Clientid, Topic]); - -cli(["delete", "clientid", Clientid, Topic])-> - case remove_acl({clientid, iolist_to_binary(Clientid)}, iolist_to_binary(Topic)) of - ok -> emqx_ctl:print("ok~n"); - {error, Reason} -> emqx_ctl:print("Error: ~p~n", [Reason]) - end; - -cli(["del", "username", Username, Topic])-> - cli(["delete", "username", Username, Topic]); - -cli(["delete", "username", Username, Topic])-> - case remove_acl({username, iolist_to_binary(Username)}, iolist_to_binary(Topic)) of - ok -> emqx_ctl:print("ok~n"); - {error, Reason} -> emqx_ctl:print("Error: ~p~n", [Reason]) - end; - -cli(["del", "_all", Topic])-> - cli(["delete", "_all", Topic]); - -cli(["delete", "_all", Topic])-> - case remove_acl(all, iolist_to_binary(Topic)) of - ok -> emqx_ctl:print("ok~n"); - {error, Reason} -> emqx_ctl:print("Error: ~p~n", [Reason]) - end; - -cli(_) -> - emqx_ctl:usage([ {"acl list clientid", "List clientid acls"} - , {"acl list username", "List username acls"} - , {"acl list _all", "List $all acls"} - , {"acl show clientid ", "Lookup clientid acl detail"} - , {"acl show username ", "Lookup username acl detail"} - , {"acl aad clientid ", "Add clientid acl"} - , {"acl add Username ", "Add username acl"} - , {"acl add _all ", "Add $all acl"} - , {"acl delete clientid ", "Delete clientid acl"} - , {"acl delete username ", "Delete username acl"} - , {"acl delete _all ", "Delete $all acl"} - ]). - -%%-------------------------------------------------------------------- -%% Internal functions -%%-------------------------------------------------------------------- - -comparing({_, _, _, _, CreatedAt1}, - {_, _, _, _, CreatedAt2}) -> - CreatedAt1 >= CreatedAt2. - -ret({atomic, ok}) -> ok; -ret({aborted, Error}) -> {error, Error}. - -validate(action, "pub") -> true; -validate(action, "sub") -> true; -validate(action, "pubsub") -> true; -validate(access, "allow") -> true; -validate(access, "deny") -> true; -validate(_, _) -> false. - -print_acl({{clientid, Clientid}, Topic, Action, Access, _}) -> - emqx_ctl:print( - "Acl(clientid = ~p topic = ~p action = ~p access = ~p)~n", - [Clientid, Topic, Action, Access] - ); -print_acl({{username, Username}, Topic, Action, Access, _}) -> - emqx_ctl:print( - "Acl(username = ~p topic = ~p action = ~p access = ~p)~n", - [Username, Topic, Action, Access] - ); -print_acl({all, Topic, Action, Access, _}) -> - emqx_ctl:print( - "Acl($all topic = ~p action = ~p access = ~p)~n", - [Topic, Action, Access] - ). - -update_permission(Action, Acl0, OldRecords) -> - Acl = Acl0 #?TABLE{action = Action}, - maybe_delete_shadowed_records(Action, OldRecords), - mnesia:write(Acl). - -maybe_delete_shadowed_records(_, []) -> - ok; -maybe_delete_shadowed_records(Action1, [Rec = #emqx_acl{action = Action2} | Rest]) -> - if Action1 =:= Action2 -> - ok = mnesia:delete_object(Rec); - Action2 =:= pubsub -> - %% Perform migration from the old data format on the - %% fly. This is needed only for the enterprise version, - %% delete this branch on 5.0 - mnesia:delete_object(Rec), - mnesia:write(Rec#?TABLE{action = other_action(Action1)}); - true -> - ok - end, - maybe_delete_shadowed_records(Action1, Rest). - -other_action(pub) -> sub; -other_action(sub) -> pub. diff --git a/apps/emqx_auth_mnesia/src/emqx_auth_mnesia.app.src b/apps/emqx_auth_mnesia/src/emqx_auth_mnesia.app.src deleted file mode 100644 index 8ff574ab5..000000000 --- a/apps/emqx_auth_mnesia/src/emqx_auth_mnesia.app.src +++ /dev/null @@ -1,14 +0,0 @@ -{application, emqx_auth_mnesia, - [{description, "EMQ X Authentication with Mnesia"}, - {vsn, "4.3.1"}, % strict semver, bump manually - {modules, []}, - {registered, []}, - {applications, [kernel,stdlib,mnesia]}, - {mod, {emqx_auth_mnesia_app,[]}}, - {env, []}, - {licenses, ["Apache-2.0"]}, - {maintainers, ["EMQ X Team "]}, - {links, [{"Homepage", "https://emqx.io/"}, - {"Github", "https://github.com/emqx/emqx-auth-mnesia"} - ]} - ]}. diff --git a/apps/emqx_auth_mnesia/src/emqx_auth_mnesia.appup.src b/apps/emqx_auth_mnesia/src/emqx_auth_mnesia.appup.src deleted file mode 100644 index 5b26c962c..000000000 --- a/apps/emqx_auth_mnesia/src/emqx_auth_mnesia.appup.src +++ /dev/null @@ -1,13 +0,0 @@ -%% -*-: erlang -*- -{VSN, - [ - {"4.3.0", [ - {restart_application, emqx_auth_mnesia} - ]} - ], - [ - {"4.3.0", [ - {restart_application, emqx_auth_mnesia} - ]} - ] -}. diff --git a/apps/emqx_auth_mnesia/src/emqx_auth_mnesia.erl b/apps/emqx_auth_mnesia/src/emqx_auth_mnesia.erl deleted file mode 100644 index 905bcaaf0..000000000 --- a/apps/emqx_auth_mnesia/src/emqx_auth_mnesia.erl +++ /dev/null @@ -1,109 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_auth_mnesia). - --include("emqx_auth_mnesia.hrl"). - --include_lib("emqx/include/emqx.hrl"). --include_lib("emqx/include/logger.hrl"). --include_lib("emqx/include/types.hrl"). - --include_lib("stdlib/include/ms_transform.hrl"). - --define(TABLE, emqx_user). -%% Auth callbacks --export([ init/1 - , register_metrics/0 - , check/3 - , description/0 - ]). - -init(#{clientid_list := ClientidList, username_list := UsernameList}) -> - ok = ekka_mnesia:create_table(?TABLE, [ - {disc_copies, [node()]}, - {attributes, record_info(fields, emqx_user)}, - {storage_properties, [{ets, [{read_concurrency, true}]}]}]), - _ = [ add_default_user({{clientid, iolist_to_binary(Clientid)}, iolist_to_binary(Password)}) - || {Clientid, Password} <- ClientidList], - _ = [ add_default_user({{username, iolist_to_binary(Username)}, iolist_to_binary(Password)}) - || {Username, Password} <- UsernameList], - ok = ekka_mnesia:copy_table(?TABLE, disc_copies). - -%% @private -add_default_user({Login, Password}) when is_tuple(Login) -> - emqx_auth_mnesia_cli:add_user(Login, Password). - --spec(register_metrics() -> ok). -register_metrics() -> - lists:foreach(fun emqx_metrics:ensure/1, ?AUTH_METRICS). - -check(ClientInfo = #{ clientid := Clientid - , password := NPassword - }, AuthResult, #{hash_type := HashType}) -> - Username = maps:get(username, ClientInfo, undefined), - MatchSpec = ets:fun2ms(fun({?TABLE, {clientid, X}, Password, InterTime}) when X =:= Clientid-> Password; - ({?TABLE, {username, X}, Password, InterTime}) when X =:= Username andalso X =/= undefined -> Password - end), - case ets:select(?TABLE, MatchSpec) of - [] -> - emqx_metrics:inc(?AUTH_METRICS(ignore)), - ok; - List -> - case match_password(NPassword, HashType, List) of - false -> - ?LOG(error, "[Mnesia] Auth from mnesia failed: ~p", [ClientInfo]), - emqx_metrics:inc(?AUTH_METRICS(failure)), - {stop, AuthResult#{anonymous => false, auth_result => password_error}}; - _ -> - emqx_metrics:inc(?AUTH_METRICS(success)), - {stop, AuthResult#{anonymous => false, auth_result => success}} - end - end. - -description() -> "Authentication with Mnesia". - -match_password(Password, HashType, HashList) -> - lists:any( - fun(Secret) -> - case is_salt_hash(Secret, HashType) of - true -> - <> = Secret, - Hash =:= hash(Password, Salt, HashType); - _ -> - Secret =:= hash(Password, HashType) - end - end, HashList). - -hash(undefined, HashType) -> - hash(<<>>, HashType); -hash(Password, HashType) -> - emqx_passwd:hash(HashType, Password). - -hash(undefined, SaltBin, HashType) -> - hash(<<>>, SaltBin, HashType); -hash(Password, SaltBin, HashType) -> - emqx_passwd:hash(HashType, <>). - -is_salt_hash(_, plain) -> - true; -is_salt_hash(Secret, HashType) -> - not (byte_size(Secret) == len(HashType)). - -len(md5) -> 32; -len(sha) -> 40; -len(sha256) -> 64; -len(sha512) -> 128. diff --git a/apps/emqx_auth_mnesia/src/emqx_auth_mnesia_api.erl b/apps/emqx_auth_mnesia/src/emqx_auth_mnesia_api.erl deleted file mode 100644 index 07ff3bdf5..000000000 --- a/apps/emqx_auth_mnesia/src/emqx_auth_mnesia_api.erl +++ /dev/null @@ -1,305 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_auth_mnesia_api). - --include_lib("stdlib/include/qlc.hrl"). --include_lib("stdlib/include/ms_transform.hrl"). - --define(TABLE, emqx_user). - --import(proplists, [get_value/2]). --import(minirest, [return/1]). --export([paginate/5]). - --export([ list_clientid/2 - , lookup_clientid/2 - , add_clientid/2 - , update_clientid/2 - , delete_clientid/2 - ]). - --rest_api(#{name => list_clientid, - method => 'GET', - path => "/auth_clientid", - func => list_clientid, - descr => "List available clientid in the cluster" - }). - --rest_api(#{name => lookup_clientid, - method => 'GET', - path => "/auth_clientid/:bin:clientid", - func => lookup_clientid, - descr => "Lookup clientid in the cluster" - }). - --rest_api(#{name => add_clientid, - method => 'POST', - path => "/auth_clientid", - func => add_clientid, - descr => "Add clientid in the cluster" - }). - --rest_api(#{name => update_clientid, - method => 'PUT', - path => "/auth_clientid/:bin:clientid", - func => update_clientid, - descr => "Update clientid in the cluster" - }). - --rest_api(#{name => delete_clientid, - method => 'DELETE', - path => "/auth_clientid/:bin:clientid", - func => delete_clientid, - descr => "Delete clientid in the cluster" - }). - --export([ list_username/2 - , lookup_username/2 - , add_username/2 - , update_username/2 - , delete_username/2 - ]). - --rest_api(#{name => list_username, - method => 'GET', - path => "/auth_username", - func => list_username, - descr => "List available username in the cluster" - }). - --rest_api(#{name => lookup_username, - method => 'GET', - path => "/auth_username/:bin:username", - func => lookup_username, - descr => "Lookup username in the cluster" - }). - --rest_api(#{name => add_username, - method => 'POST', - path => "/auth_username", - func => add_username, - descr => "Add username in the cluster" - }). - --rest_api(#{name => update_username, - method => 'PUT', - path => "/auth_username/:bin:username", - func => update_username, - descr => "Update username in the cluster" - }). - --rest_api(#{name => delete_username, - method => 'DELETE', - path => "/auth_username/:bin:username", - func => delete_username, - descr => "Delete username in the cluster" - }). - -%%------------------------------------------------------------------------------ -%% Auth Clientid Api -%%------------------------------------------------------------------------------ - -list_clientid(_Bindings, Params) -> - MatchSpec = ets:fun2ms(fun({?TABLE, {clientid, Clientid}, Password, CreatedAt}) -> {?TABLE, {clientid, Clientid}, Password, CreatedAt} end), - return({ok, paginate(?TABLE, MatchSpec, Params, fun emqx_auth_mnesia_cli:comparing/2, fun({?TABLE, {clientid, X}, _, _}) -> #{clientid => X} end)}). - -lookup_clientid(#{clientid := Clientid}, _Params) -> - return({ok, format(emqx_auth_mnesia_cli:lookup_user({clientid, urldecode(Clientid)}))}). - -add_clientid(_Bindings, Params) -> - [ P | _] = Params, - case is_list(P) of - true -> return(do_add_clientid(Params, [])); - false -> - Re = do_add_clientid(Params), - case Re of - ok -> return(ok); - {error, Error} -> return({error, format_msg(Error)}) - end - end. - -do_add_clientid([ Params | ParamsN ], ReList ) -> - Clientid = urldecode(get_value(<<"clientid">>, Params)), - do_add_clientid(ParamsN, [{Clientid, format_msg(do_add_clientid(Params))} | ReList]); - -do_add_clientid([], ReList) -> - {ok, ReList}. - -do_add_clientid(Params) -> - Clientid = urldecode(get_value(<<"clientid">>, Params)), - Password = urldecode(get_value(<<"password">>, Params)), - Login = {clientid, Clientid}, - case validate([login, password], [Login, Password]) of - ok -> - emqx_auth_mnesia_cli:add_user(Login, Password); - Err -> Err - end. - -update_clientid(#{clientid := Clientid}, Params) -> - Password = get_value(<<"password">>, Params), - case validate([password], [Password]) of - ok -> return(emqx_auth_mnesia_cli:update_user({clientid, urldecode(Clientid)}, urldecode(Password))); - Err -> return(Err) - end. - -delete_clientid(#{clientid := Clientid}, _) -> - return(emqx_auth_mnesia_cli:remove_user({clientid, urldecode(Clientid)})). - -%%------------------------------------------------------------------------------ -%% Auth Username Api -%%------------------------------------------------------------------------------ - -list_username(_Bindings, Params) -> - MatchSpec = ets:fun2ms(fun({?TABLE, {username, Username}, Password, CreatedAt}) -> {?TABLE, {username, Username}, Password, CreatedAt} end), - return({ok, paginate(?TABLE, MatchSpec, Params, fun emqx_auth_mnesia_cli:comparing/2, fun({?TABLE, {username, X}, _, _}) -> #{username => X} end)}). - -lookup_username(#{username := Username}, _Params) -> - return({ok, format(emqx_auth_mnesia_cli:lookup_user({username, urldecode(Username)}))}). - -add_username(_Bindings, Params) -> - [ P | _] = Params, - case is_list(P) of - true -> return(do_add_username(Params, [])); - false -> - case do_add_username(Params) of - ok -> return(ok); - {error, Error} -> return({error, format_msg(Error)}) - end - end. - -do_add_username([ Params | ParamsN ], ReList ) -> - Username = urldecode(get_value(<<"username">>, Params)), - do_add_username(ParamsN, [{Username, format_msg(do_add_username(Params))} | ReList]); - -do_add_username([], ReList) -> - {ok, ReList}. - -do_add_username(Params) -> - Username = urldecode(get_value(<<"username">>, Params)), - Password = urldecode(get_value(<<"password">>, Params)), - Login = {username, Username}, - case validate([login, password], [Login, Password]) of - ok -> - emqx_auth_mnesia_cli:add_user(Login, Password); - Err -> Err - end. - -update_username(#{username := Username}, Params) -> - Password = get_value(<<"password">>, Params), - case validate([password], [Password]) of - ok -> return(emqx_auth_mnesia_cli:update_user({username, urldecode(Username)}, urldecode(Password))); - Err -> return(Err) - end. - -delete_username(#{username := Username}, _) -> - return(emqx_auth_mnesia_cli:remove_user({username, urldecode(Username)})). - -%%------------------------------------------------------------------------------ -%% Paging Query -%%------------------------------------------------------------------------------ - -paginate(Tables, MatchSpec, Params, ComparingFun, RowFun) -> - Qh = query_handle(Tables, MatchSpec), - Count = count(Tables, MatchSpec), - Page = page(Params), - Limit = limit(Params), - Cursor = qlc:cursor(Qh), - case Page > 1 of - true -> - _ = qlc:next_answers(Cursor, (Page - 1) * Limit), - ok; - false -> ok - end, - Rows = qlc:next_answers(Cursor, Limit), - qlc:delete_cursor(Cursor), - #{meta => #{page => Page, limit => Limit, count => Count}, - data => [RowFun(Row) || Row <- lists:sort(ComparingFun, Rows)]}. - -query_handle(Table, MatchSpec) when is_atom(Table) -> - Options = {traverse, {select, MatchSpec}}, - qlc:q([R|| R <- ets:table(Table, Options)]); -query_handle([Table], MatchSpec) when is_atom(Table) -> - Options = {traverse, {select, MatchSpec}}, - qlc:q([R|| R <- ets:table(Table, Options)]); -query_handle(Tables, MatchSpec) -> - Options = {traverse, {select, MatchSpec}}, - qlc:append([qlc:q([E || E <- ets:table(T, Options)]) || T <- Tables]). - -count(Table, MatchSpec) when is_atom(Table) -> - [{MatchPattern, Where, _Re}] = MatchSpec, - NMatchSpec = [{MatchPattern, Where, [true]}], - ets:select_count(Table, NMatchSpec); -count([Table], MatchSpec) when is_atom(Table) -> - [{MatchPattern, Where, _Re}] = MatchSpec, - NMatchSpec = [{MatchPattern, Where, [true]}], - ets:select_count(Table, NMatchSpec); -count(Tables, MatchSpec) -> - lists:sum([count(T, MatchSpec) || T <- Tables]). - -page(Params) -> - binary_to_integer(proplists:get_value(<<"_page">>, Params, <<"1">>)). - -limit(Params) -> - case proplists:get_value(<<"_limit">>, Params) of - undefined -> 10; - Size -> binary_to_integer(Size) - end. - -%%------------------------------------------------------------------------------ -%% Interval Funcs -%%------------------------------------------------------------------------------ - -format([{?TABLE, {clientid, ClientId}, Password, _InterTime}]) -> - #{clientid => ClientId, - password => Password}; - -format([{?TABLE, {username, Username}, Password, _InterTime}]) -> - #{username => Username, - password => Password}; - -format([]) -> - #{}. - -validate([], []) -> - ok; -validate([K|Keys], [V|Values]) -> - case do_validation(K, V) of - false -> {error, K}; - true -> validate(Keys, Values) - end. - -do_validation(login, {clientid, V}) when is_binary(V) - andalso byte_size(V) > 0 -> - true; -do_validation(login, {username, V}) when is_binary(V) - andalso byte_size(V) > 0 -> - true; -do_validation(password, V) when is_binary(V) - andalso byte_size(V) > 0 -> - true; -do_validation(_, _) -> - false. - -format_msg(Message) - when is_atom(Message); - is_binary(Message) -> Message; - -format_msg(Message) when is_tuple(Message) -> - iolist_to_binary(io_lib:format("~p", [Message])). - -urldecode(S) -> - emqx_http_lib:uri_decode(S). diff --git a/apps/emqx_auth_mnesia/src/emqx_auth_mnesia_app.erl b/apps/emqx_auth_mnesia/src/emqx_auth_mnesia_app.erl deleted file mode 100644 index 91f4bdf4f..000000000 --- a/apps/emqx_auth_mnesia/src/emqx_auth_mnesia_app.erl +++ /dev/null @@ -1,68 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_auth_mnesia_app). - --behaviour(application). - --emqx_plugin(auth). - --include("emqx_auth_mnesia.hrl"). - -%% Application callbacks --export([ start/2 - , prep_stop/1 - , stop/1 - ]). - -%%-------------------------------------------------------------------- -%% Application callbacks -%%-------------------------------------------------------------------- - -start(_StartType, _StartArgs) -> - {ok, Sup} = emqx_auth_mnesia_sup:start_link(), - emqx_ctl:register_command(clientid, {emqx_auth_mnesia_cli, auth_clientid_cli}, []), - emqx_ctl:register_command(user, {emqx_auth_mnesia_cli, auth_username_cli}, []), - emqx_ctl:register_command(acl, {emqx_acl_mnesia_cli, cli}, []), - _ = load_auth_hook(), - _ = load_acl_hook(), - {ok, Sup}. - -prep_stop(State) -> - emqx:unhook('client.authenticate', {emqx_auth_mnesia, check}), - emqx:unhook('client.check_acl', {emqx_acl_mnesia, check_acl}), - emqx_ctl:unregister_command(clientid), - emqx_ctl:unregister_command(user), - emqx_ctl:unregister_command(acl), - State. - -stop(_State) -> - ok. - -load_auth_hook() -> - ClientidList = application:get_env(?APP, clientid_list, []), - UsernameList = application:get_env(?APP, username_list, []), - ok = emqx_auth_mnesia:init(#{clientid_list => ClientidList, username_list => UsernameList}), - ok = emqx_auth_mnesia:register_metrics(), - Params = #{ - hash_type => application:get_env(emqx_auth_mnesia, password_hash, sha256) - }, - emqx:hook('client.authenticate', {emqx_auth_mnesia, check, [Params]}). - -load_acl_hook() -> - ok = emqx_acl_mnesia:init(), - ok = emqx_acl_mnesia:register_metrics(), - emqx:hook('client.check_acl', {emqx_acl_mnesia, check_acl, [#{}]}). diff --git a/apps/emqx_auth_mnesia/src/emqx_auth_mnesia_cli.erl b/apps/emqx_auth_mnesia/src/emqx_auth_mnesia_cli.erl deleted file mode 100644 index d89e6836c..000000000 --- a/apps/emqx_auth_mnesia/src/emqx_auth_mnesia_cli.erl +++ /dev/null @@ -1,194 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_auth_mnesia_cli). - --include("emqx_auth_mnesia.hrl"). --include_lib("emqx/include/logger.hrl"). --include_lib("stdlib/include/ms_transform.hrl"). --define(TABLE, emqx_user). -%% Auth APIs --export([ add_user/2 - , update_user/2 - , remove_user/1 - , lookup_user/1 - , all_users/0 - , all_users/1 - ]). -%% Cli --export([ auth_clientid_cli/1 - , auth_username_cli/1 - ]). - -%% Helper --export([comparing/2]). - -%%-------------------------------------------------------------------- -%% Auth APIs -%%-------------------------------------------------------------------- - -%% @doc Add User --spec(add_user(tuple(), binary()) -> ok | {error, any()}). -add_user(Login, Password) -> - User = #emqx_user{ - login = Login, - password = encrypted_data(Password), - created_at = erlang:system_time(millisecond) - }, - ret(mnesia:transaction(fun insert_user/1, [User])). - -insert_user(User = #emqx_user{login = Login}) -> - case mnesia:read(?TABLE, Login) of - [] -> mnesia:write(User); - [_|_] -> mnesia:abort(existed) - end. - -%% @doc Update User --spec(update_user(tuple(), binary()) -> ok | {error, any()}). -update_user(Login, NewPassword) -> - ret(mnesia:transaction(fun do_update_user/2, [Login, encrypted_data(NewPassword)])). - -do_update_user(Login, NewPassword) -> - case mnesia:read(?TABLE, Login) of - [#emqx_user{} = User] -> - mnesia:write(User#emqx_user{password = NewPassword}); - [] -> mnesia:abort(noexisted) - end. - -%% @doc Lookup user by login --spec(lookup_user(tuple()) -> list()). -lookup_user(undefined) -> []; -lookup_user(Login) -> - Re = mnesia:dirty_read(?TABLE, Login), - lists:sort(fun comparing/2, Re). - -%% @doc Remove user --spec(remove_user(tuple()) -> ok | {error, any()}). -remove_user(Login) -> - ret(mnesia:transaction(fun mnesia:delete/1, [{?TABLE, Login}])). - -%% @doc All logins --spec(all_users() -> list()). -all_users() -> mnesia:dirty_all_keys(?TABLE). - -all_users(clientid) -> - MatchSpec = ets:fun2ms( - fun({?TABLE, {clientid, Clientid}, Password, CreatedAt}) -> - {?TABLE, {clientid, Clientid}, Password, CreatedAt} - end), - lists:sort(fun comparing/2, ets:select(?TABLE, MatchSpec)); -all_users(username) -> - MatchSpec = ets:fun2ms( - fun({?TABLE, {username, Username}, Password, CreatedAt}) -> - {?TABLE, {username, Username}, Password, CreatedAt} - end), - lists:sort(fun comparing/2, ets:select(?TABLE, MatchSpec)). - -%%-------------------------------------------------------------------- -%% Internal functions -%%-------------------------------------------------------------------- - -comparing({?TABLE, _, _, CreatedAt1}, - {?TABLE, _, _, CreatedAt2}) -> - CreatedAt1 >= CreatedAt2. - -ret({atomic, ok}) -> ok; -ret({aborted, Error}) -> {error, Error}. - -encrypted_data(Password) -> - HashType = application:get_env(emqx_auth_mnesia, password_hash, sha256), - SaltBin = salt(), - <>. - -hash(undefined, SaltBin, HashType) -> - hash(<<>>, SaltBin, HashType); -hash(Password, SaltBin, HashType) -> - emqx_passwd:hash(HashType, <>). - -salt() -> - {_AlgHandler, _AlgState} = rand:seed(exsplus, erlang:timestamp()), - Salt = rand:uniform(16#ffffffff), <>. - -%%-------------------------------------------------------------------- -%% Auth Clientid Cli -%%-------------------------------------------------------------------- - -auth_clientid_cli(["list"]) -> - [emqx_ctl:print("~s~n", [ClientId]) - || {?TABLE, {clientid, ClientId}, _Password, _CreatedAt} <- all_users(clientid) - ]; - -auth_clientid_cli(["add", ClientId, Password]) -> - case add_user({clientid, iolist_to_binary(ClientId)}, iolist_to_binary(Password)) of - ok -> emqx_ctl:print("ok~n"); - {error, Reason} -> emqx_ctl:print("Error: ~p~n", [Reason]) - end; - -auth_clientid_cli(["update", ClientId, NewPassword]) -> - case update_user({clientid, iolist_to_binary(ClientId)}, iolist_to_binary(NewPassword)) of - ok -> emqx_ctl:print("ok~n"); - {error, Reason} -> emqx_ctl:print("Error: ~p~n", [Reason]) - end; - -auth_clientid_cli(["del", ClientId]) -> - auth_clientid_cli(["delete", ClientId]); - -auth_clientid_cli(["delete", ClientId]) -> - case remove_user({clientid, iolist_to_binary(ClientId)}) of - ok -> emqx_ctl:print("ok~n"); - {error, Reason} -> emqx_ctl:print("Error: ~p~n", [Reason]) - end; - -auth_clientid_cli(_) -> - emqx_ctl:usage([{"clientid list", "List clientid auth rules"}, - {"clientid add ", "Add clientid auth rule"}, - {"clientid update ", "Update clientid auth rule"}, - {"clientid delete ", "Delete clientid auth rule"}]). - -%%-------------------------------------------------------------------- -%% Auth Username Cli -%%-------------------------------------------------------------------- - -auth_username_cli(["list"]) -> - [emqx_ctl:print("~s~n", [Username]) - || {?TABLE, {username, Username}, _Password, _CreatedAt} <- all_users(username) - ]; - -auth_username_cli(["add", Username, Password]) -> - case add_user({username, iolist_to_binary(Username)}, iolist_to_binary(Password)) of - ok -> emqx_ctl:print("ok~n"); - {error, Reason} -> emqx_ctl:print("Error: ~p~n", [Reason]) - end; - -auth_username_cli(["update", Username, NewPassword]) -> - case update_user({username, iolist_to_binary(Username)}, iolist_to_binary(NewPassword)) of - ok -> emqx_ctl:print("ok~n"); - {error, Reason} -> emqx_ctl:print("Error: ~p~n", [Reason]) - end; -auth_username_cli(["del", Username]) -> - auth_username_cli(["delete", Username]); - -auth_username_cli(["delete", Username]) -> - case remove_user({username, iolist_to_binary(Username)}) of - ok -> emqx_ctl:print("ok~n"); - {error, Reason} -> emqx_ctl:print("Error: ~p~n", [Reason]) - end; - -auth_username_cli(_) -> - emqx_ctl:usage([{"user list", "List username auth rules"}, - {"user add ", "Add username auth rule"}, - {"user update ", "Update username auth rule"}, - {"user delete ", "Delete username auth rule"}]). diff --git a/apps/emqx_auth_mnesia/src/emqx_auth_mnesia_sup.erl b/apps/emqx_auth_mnesia/src/emqx_auth_mnesia_sup.erl deleted file mode 100644 index 3784eaaf6..000000000 --- a/apps/emqx_auth_mnesia/src/emqx_auth_mnesia_sup.erl +++ /dev/null @@ -1,36 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_auth_mnesia_sup). - --behaviour(supervisor). - --include("emqx_auth_mnesia.hrl"). - --export([start_link/0]). - -%% Supervisor callbacks --export([init/1]). - -start_link() -> - supervisor:start_link({local, ?MODULE}, ?MODULE, []). - -%%-------------------------------------------------------------------- -%% Supervisor callbacks -%%-------------------------------------------------------------------- - -init([]) -> - {ok, {{one_for_one, 10, 100}, []}}. \ No newline at end of file diff --git a/apps/emqx_auth_mnesia/test/emqx_acl_mnesia_SUITE.erl b/apps/emqx_auth_mnesia/test/emqx_acl_mnesia_SUITE.erl deleted file mode 100644 index f7994387b..000000000 --- a/apps/emqx_auth_mnesia/test/emqx_acl_mnesia_SUITE.erl +++ /dev/null @@ -1,293 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_acl_mnesia_SUITE). - --compile(nowarn_export_all). --compile(export_all). - --include("emqx_auth_mnesia.hrl"). --include_lib("eunit/include/eunit.hrl"). --include_lib("common_test/include/ct.hrl"). - --import(emqx_ct_http, [ request_api/3 - , request_api/5 - , get_http_data/1 - , create_default_app/0 - , delete_default_app/0 - , default_auth_header/0 - ]). - --define(HOST, "http://127.0.0.1:8081/"). --define(API_VERSION, "v4"). --define(BASE_PATH, "api"). - -all() -> - emqx_ct:all(?MODULE). - -groups() -> - []. - -init_per_suite(Config) -> - emqx_ct_helpers:start_apps([emqx_modules, emqx_management, emqx_auth_mnesia], fun set_special_configs/1), - create_default_app(), - Config. - -end_per_suite(_Config) -> - delete_default_app(), - emqx_ct_helpers:stop_apps([emqx_modules, emqx_management, emqx_auth_mnesia]). - -set_special_configs(emqx) -> - application:set_env(emqx, allow_anonymous, true), - application:set_env(emqx, enable_acl_cache, false), - LoadedPluginPath = filename:join(["test", "emqx_SUITE_data", "loaded_plugins"]), - application:set_env(emqx, plugins_loaded_file, - emqx_ct_helpers:deps_path(emqx, LoadedPluginPath)); - -set_special_configs(_App) -> - ok. - -%%------------------------------------------------------------------------------ -%% Testcases -%%------------------------------------------------------------------------------ - -t_management(_Config) -> - clean_all_acls(), - ?assertEqual("Acl with Mnesia", emqx_acl_mnesia:description()), - ?assertEqual([], emqx_acl_mnesia_cli:all_acls()), - - ok = emqx_acl_mnesia_cli:add_acl({clientid, <<"test_clientid">>}, <<"topic/%c">>, sub, allow), - ok = emqx_acl_mnesia_cli:add_acl({clientid, <<"test_clientid">>}, <<"topic/+">>, pub, deny), - ok = emqx_acl_mnesia_cli:add_acl({username, <<"test_username">>}, <<"topic/%u">>, sub, deny), - ok = emqx_acl_mnesia_cli:add_acl({username, <<"test_username">>}, <<"topic/+">>, pub, allow), - ok = emqx_acl_mnesia_cli:add_acl(all, <<"#">>, pubsub, deny), - %% Sleeps below are needed to hide the race condition between - %% mnesia and ets dirty select in check_acl, that make this test - %% flaky - timer:sleep(100), - - ?assertEqual(2, length(emqx_acl_mnesia_cli:lookup_acl({clientid, <<"test_clientid">>}))), - ?assertEqual(2, length(emqx_acl_mnesia_cli:lookup_acl({username, <<"test_username">>}))), - ?assertEqual(2, length(emqx_acl_mnesia_cli:lookup_acl(all))), - ?assertEqual(6, length(emqx_acl_mnesia_cli:all_acls())), - - User1 = #{zone => external, clientid => <<"test_clientid">>}, - User2 = #{zone => external, clientid => <<"no_exist">>, username => <<"test_username">>}, - User3 = #{zone => external, clientid => <<"test_clientid">>, username => <<"test_username">>}, - allow = emqx_access_control:check_acl(User1, subscribe, <<"topic/test_clientid">>), - deny = emqx_access_control:check_acl(User1, publish, <<"topic/A">>), - deny = emqx_access_control:check_acl(User2, subscribe, <<"topic/test_username">>), - allow = emqx_access_control:check_acl(User2, publish, <<"topic/A">>), - allow = emqx_access_control:check_acl(User3, subscribe, <<"topic/test_clientid">>), - deny = emqx_access_control:check_acl(User3, subscribe, <<"topic/test_username">>), - deny = emqx_access_control:check_acl(User3, publish, <<"topic/A">>), - deny = emqx_access_control:check_acl(User3, subscribe, <<"topic/A/B">>), - deny = emqx_access_control:check_acl(User3, publish, <<"topic/A/B">>), - - %% Test merging of pubsub capability: - ok = emqx_acl_mnesia_cli:add_acl({clientid, <<"test_clientid">>}, <<"topic/mix">>, pubsub, deny), - timer:sleep(100), - deny = emqx_access_control:check_acl(User1, subscribe, <<"topic/mix">>), - deny = emqx_access_control:check_acl(User1, publish, <<"topic/mix">>), - ok = emqx_acl_mnesia_cli:add_acl({clientid, <<"test_clientid">>}, <<"topic/mix">>, pub, allow), - timer:sleep(100), - deny = emqx_access_control:check_acl(User1, subscribe, <<"topic/mix">>), - allow = emqx_access_control:check_acl(User1, publish, <<"topic/mix">>), - ok = emqx_acl_mnesia_cli:add_acl({clientid, <<"test_clientid">>}, <<"topic/mix">>, pubsub, allow), - timer:sleep(100), - allow = emqx_access_control:check_acl(User1, subscribe, <<"topic/mix">>), - allow = emqx_access_control:check_acl(User1, publish, <<"topic/mix">>), - ok = emqx_acl_mnesia_cli:add_acl({clientid, <<"test_clientid">>}, <<"topic/mix">>, sub, deny), - timer:sleep(100), - deny = emqx_access_control:check_acl(User1, subscribe, <<"topic/mix">>), - allow = emqx_access_control:check_acl(User1, publish, <<"topic/mix">>), - ok = emqx_acl_mnesia_cli:add_acl({clientid, <<"test_clientid">>}, <<"topic/mix">>, pub, deny), - timer:sleep(100), - deny = emqx_access_control:check_acl(User1, subscribe, <<"topic/mix">>), - deny = emqx_access_control:check_acl(User1, publish, <<"topic/mix">>), - - %% Test implicit migration of pubsub to pub and sub: - ok = emqx_acl_mnesia_cli:remove_acl({clientid, <<"test_clientid">>}, <<"topic/mix">>), - ok = mnesia:dirty_write(#emqx_acl{ - filter = {{clientid, <<"test_clientid">>}, <<"topic/mix">>}, - action = pubsub, - access = allow, - created_at = erlang:system_time(millisecond) - }), - timer:sleep(100), - allow = emqx_access_control:check_acl(User1, subscribe, <<"topic/mix">>), - allow = emqx_access_control:check_acl(User1, publish, <<"topic/mix">>), - ok = emqx_acl_mnesia_cli:add_acl({clientid, <<"test_clientid">>}, <<"topic/mix">>, pub, deny), - timer:sleep(100), - allow = emqx_access_control:check_acl(User1, subscribe, <<"topic/mix">>), - deny = emqx_access_control:check_acl(User1, publish, <<"topic/mix">>), - ok = emqx_acl_mnesia_cli:add_acl({clientid, <<"test_clientid">>}, <<"topic/mix">>, sub, deny), - timer:sleep(100), - deny = emqx_access_control:check_acl(User1, subscribe, <<"topic/mix">>), - deny = emqx_access_control:check_acl(User1, publish, <<"topic/mix">>), - - ok = emqx_acl_mnesia_cli:remove_acl({clientid, <<"test_clientid">>}, <<"topic/%c">>), - ok = emqx_acl_mnesia_cli:remove_acl({clientid, <<"test_clientid">>}, <<"topic/+">>), - ok = emqx_acl_mnesia_cli:remove_acl({clientid, <<"test_clientid">>}, <<"topic/mix">>), - ok = emqx_acl_mnesia_cli:remove_acl({username, <<"test_username">>}, <<"topic/%u">>), - ok = emqx_acl_mnesia_cli:remove_acl({username, <<"test_username">>}, <<"topic/+">>), - ok = emqx_acl_mnesia_cli:remove_acl(all, <<"#">>), - timer:sleep(100), - - ?assertEqual([], emqx_acl_mnesia_cli:all_acls()). - -t_acl_cli(_Config) -> - meck:new(emqx_ctl, [non_strict, passthrough]), - meck:expect(emqx_ctl, print, fun(Arg) -> emqx_ctl:format(Arg) end), - meck:expect(emqx_ctl, print, fun(Msg, Arg) -> emqx_ctl:format(Msg, Arg) end), - meck:expect(emqx_ctl, usage, fun(Usages) -> emqx_ctl:format_usage(Usages) end), - meck:expect(emqx_ctl, usage, fun(Cmd, Descr) -> emqx_ctl:format_usage(Cmd, Descr) end), - - clean_all_acls(), - - ?assertEqual(0, length(emqx_acl_mnesia_cli:cli(["list"]))), - - emqx_acl_mnesia_cli:cli(["add", "clientid", "test_clientid", "topic/A", "pub", "deny"]), - emqx_acl_mnesia_cli:cli(["add", "clientid", "test_clientid", "topic/A", "pub", "allow"]), - R1 = emqx_ctl:format("Acl(clientid = ~p topic = ~p action = ~p access = ~p)~n", - [<<"test_clientid">>, <<"topic/A">>, pub, allow]), - ?assertEqual([R1], emqx_acl_mnesia_cli:cli(["show", "clientid", "test_clientid"])), - ?assertEqual([R1], emqx_acl_mnesia_cli:cli(["list", "clientid"])), - - emqx_acl_mnesia_cli:cli(["add", "username", "test_username", "topic/B", "sub", "deny"]), - R2 = emqx_ctl:format("Acl(username = ~p topic = ~p action = ~p access = ~p)~n", - [<<"test_username">>, <<"topic/B">>, sub, deny]), - ?assertEqual([R2], emqx_acl_mnesia_cli:cli(["show", "username", "test_username"])), - ?assertEqual([R2], emqx_acl_mnesia_cli:cli(["list", "username"])), - - emqx_acl_mnesia_cli:cli(["add", "_all", "#", "pub", "allow"]), - emqx_acl_mnesia_cli:cli(["add", "_all", "#", "pubsub", "deny"]), - ?assertMatch(["", - "Acl($all topic = <<\"#\">> action = pub access = deny)", - "Acl($all topic = <<\"#\">> action = sub access = deny)"], - lists:sort(string:split(emqx_acl_mnesia_cli:cli(["list", "_all"]), "\n", all)) - ), - ?assertEqual(4, length(emqx_acl_mnesia_cli:cli(["list"]))), - - emqx_acl_mnesia_cli:cli(["del", "clientid", "test_clientid", "topic/A"]), - emqx_acl_mnesia_cli:cli(["del", "username", "test_username", "topic/B"]), - emqx_acl_mnesia_cli:cli(["del", "_all", "#"]), - ?assertEqual(0, length(emqx_acl_mnesia_cli:cli(["list"]))), - - meck:unload(emqx_ctl). - -t_rest_api(_Config) -> - clean_all_acls(), - - Params1 = [#{<<"clientid">> => <<"test_clientid">>, - <<"topic">> => <<"topic/A">>, - <<"action">> => <<"pub">>, - <<"access">> => <<"allow">> - }, - #{<<"clientid">> => <<"test_clientid">>, - <<"topic">> => <<"topic/B">>, - <<"action">> => <<"sub">>, - <<"access">> => <<"allow">> - }, - #{<<"clientid">> => <<"test_clientid">>, - <<"topic">> => <<"topic/C">>, - <<"action">> => <<"pubsub">>, - <<"access">> => <<"deny">> - }], - {ok, _} = request_http_rest_add([], Params1), - {ok, Re1} = request_http_rest_list(["clientid", "test_clientid"]), - ?assertMatch(4, length(get_http_data(Re1))), - {ok, _} = request_http_rest_delete(["clientid", "test_clientid", "topic", "topic/A"]), - {ok, _} = request_http_rest_delete(["clientid", "test_clientid", "topic", "topic/B"]), - {ok, _} = request_http_rest_delete(["clientid", "test_clientid", "topic", "topic/C"]), - {ok, Res1} = request_http_rest_list(["clientid"]), - ?assertMatch([], get_http_data(Res1)), - - Params2 = [#{<<"username">> => <<"test_username">>, - <<"topic">> => <<"topic/A">>, - <<"action">> => <<"pub">>, - <<"access">> => <<"allow">> - }, - #{<<"username">> => <<"test_username">>, - <<"topic">> => <<"topic/B">>, - <<"action">> => <<"sub">>, - <<"access">> => <<"allow">> - }, - #{<<"username">> => <<"test_username">>, - <<"topic">> => <<"topic/C">>, - <<"action">> => <<"pubsub">>, - <<"access">> => <<"deny">> - }], - {ok, _} = request_http_rest_add([], Params2), - {ok, Re2} = request_http_rest_list(["username", "test_username"]), - ?assertMatch(4, length(get_http_data(Re2))), - {ok, _} = request_http_rest_delete(["username", "test_username", "topic", "topic/A"]), - {ok, _} = request_http_rest_delete(["username", "test_username", "topic", "topic/B"]), - {ok, _} = request_http_rest_delete(["username", "test_username", "topic", "topic/C"]), - {ok, Res2} = request_http_rest_list(["username"]), - ?assertMatch([], get_http_data(Res2)), - - Params3 = [#{<<"topic">> => <<"topic/A">>, - <<"action">> => <<"pub">>, - <<"access">> => <<"allow">> - }, - #{<<"topic">> => <<"topic/B">>, - <<"action">> => <<"sub">>, - <<"access">> => <<"allow">> - }, - #{<<"topic">> => <<"topic/C">>, - <<"action">> => <<"pubsub">>, - <<"access">> => <<"deny">> - }], - {ok, _} = request_http_rest_add([], Params3), - {ok, Re3} = request_http_rest_list(["$all"]), - ?assertMatch(4, length(get_http_data(Re3))), - {ok, _} = request_http_rest_delete(["$all", "topic", "topic/A"]), - {ok, _} = request_http_rest_delete(["$all", "topic", "topic/B"]), - {ok, _} = request_http_rest_delete(["$all", "topic", "topic/C"]), - {ok, Res3} = request_http_rest_list(["$all"]), - ?assertMatch([], get_http_data(Res3)). - -%%------------------------------------------------------------------------------ -%% Helpers -%%------------------------------------------------------------------------------ - -clean_all_acls() -> - [ mnesia:dirty_delete({emqx_acl, Login}) - || Login <- mnesia:dirty_all_keys(emqx_acl)]. - -%%-------------------------------------------------------------------- -%% HTTP Request -%%-------------------------------------------------------------------- - -request_http_rest_list(Path) -> - request_api(get, uri(Path), default_auth_header()). - -request_http_rest_lookup(Path) -> - request_api(get, uri(Path), default_auth_header()). - -request_http_rest_add(Path, Params) -> - request_api(post, uri(Path), [], default_auth_header(), Params). - -request_http_rest_delete(Path) -> - request_api(delete, uri(Path), default_auth_header()). - -uri() -> uri([]). -uri(Parts) when is_list(Parts) -> - NParts = [b2l(E) || E <- Parts], - ?HOST ++ filename:join([?BASE_PATH, ?API_VERSION, "acl"| NParts]). - -b2l(B) -> binary_to_list(emqx_http_lib:uri_encode(iolist_to_binary(B))). diff --git a/apps/emqx_auth_mnesia/test/emqx_auth_mnesia_SUITE.erl b/apps/emqx_auth_mnesia/test/emqx_auth_mnesia_SUITE.erl deleted file mode 100644 index c5c0eb727..000000000 --- a/apps/emqx_auth_mnesia/test/emqx_auth_mnesia_SUITE.erl +++ /dev/null @@ -1,318 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_auth_mnesia_SUITE). - --compile(nowarn_export_all). --compile(export_all). - --include("emqx_auth_mnesia.hrl"). --include_lib("eunit/include/eunit.hrl"). --include_lib("common_test/include/ct.hrl"). - --import(emqx_ct_http, [ request_api/3 - , request_api/5 - , get_http_data/1 - , create_default_app/0 - , delete_default_app/0 - , default_auth_header/0 - ]). - --define(HOST, "http://127.0.0.1:8081/"). --define(API_VERSION, "v4"). --define(BASE_PATH, "api"). - --define(TABLE, emqx_user). --define(CLIENTID, <<"clientid_for_ct">>). --define(USERNAME, <<"username_for_ct">>). --define(PASSWORD, <<"password">>). --define(NPASSWORD, <<"new_password">>). - -all() -> - emqx_ct:all(?MODULE). - -groups() -> - []. - -init_per_suite(Config) -> - ok = emqx_ct_helpers:start_apps([emqx_management, emqx_auth_mnesia], fun set_special_configs/1), - create_default_app(), - Config. - -end_per_suite(_Config) -> - delete_default_app(), - emqx_ct_helpers:stop_apps([emqx_management, emqx_auth_mnesia]). - -set_special_configs(emqx) -> - application:set_env(emqx, allow_anonymous, true), - application:set_env(emqx, enable_acl_cache, false), - LoadedPluginPath = filename:join(["test", "emqx_SUITE_data", "loaded_plugins"]), - application:set_env(emqx, plugins_loaded_file, - emqx_ct_helpers:deps_path(emqx, LoadedPluginPath)); - -set_special_configs(_App) -> - ok. - -%%------------------------------------------------------------------------------ -%% Testcases -%%------------------------------------------------------------------------------ - -t_management(_Config) -> - clean_all_users(), - - ok = emqx_auth_mnesia_cli:add_user({username, ?USERNAME}, ?PASSWORD), - {error, existed} = emqx_auth_mnesia_cli:add_user({username, ?USERNAME}, ?PASSWORD), - ?assertMatch([{?TABLE, {username, ?USERNAME}, _, _}], - emqx_auth_mnesia_cli:all_users(username) - ), - - ok = emqx_auth_mnesia_cli:add_user({clientid, ?CLIENTID}, ?PASSWORD), - {error, existed} = emqx_auth_mnesia_cli:add_user({clientid, ?CLIENTID}, ?PASSWORD), - ?assertMatch([{?TABLE, {clientid, ?CLIENTID}, _, _}], - emqx_auth_mnesia_cli:all_users(clientid) - ), - - ?assertEqual(2, length(emqx_auth_mnesia_cli:all_users())), - - ok = emqx_auth_mnesia_cli:update_user({username, ?USERNAME}, ?NPASSWORD), - {error, noexisted} = emqx_auth_mnesia_cli:update_user( - {username, <<"no_existed_user">>}, ?PASSWORD - ), - - ok = emqx_auth_mnesia_cli:update_user({clientid, ?CLIENTID}, ?NPASSWORD), - {error, noexisted} = emqx_auth_mnesia_cli:update_user( - {clientid, <<"no_existed_user">>}, ?PASSWORD - ), - - ?assertMatch([{?TABLE, {username, ?USERNAME}, _, _}], - emqx_auth_mnesia_cli:lookup_user({username, ?USERNAME}) - ), - ?assertMatch([{?TABLE, {clientid, ?CLIENTID}, _, _}], - emqx_auth_mnesia_cli:lookup_user({clientid, ?CLIENTID}) - ), - - User1 = #{username => ?USERNAME, - clientid => undefined, - password => ?NPASSWORD, - zone => external}, - - {ok, #{auth_result := success, - anonymous := false}} = emqx_access_control:authenticate(User1), - - {error, password_error} = emqx_access_control:authenticate( - User1#{password => <<"error_password">>} - ), - - ok = emqx_auth_mnesia_cli:remove_user({username, ?USERNAME}), - {ok, #{auth_result := success, - anonymous := true }} = emqx_access_control:authenticate(User1), - - User2 = #{clientid => ?CLIENTID, - password => ?NPASSWORD, - zone => external}, - - {ok, #{auth_result := success, - anonymous := false}} = emqx_access_control:authenticate(User2), - - {error, password_error} = emqx_access_control:authenticate( - User2#{password => <<"error_password">>} - ), - - ok = emqx_auth_mnesia_cli:remove_user({clientid, ?CLIENTID}), - {ok, #{auth_result := success, - anonymous := true }} = emqx_access_control:authenticate(User2), - - [] = emqx_auth_mnesia_cli:all_users(). - -t_auth_clientid_cli(_) -> - clean_all_users(), - - HashType = application:get_env(emqx_auth_mnesia, password_hash, sha256), - - emqx_auth_mnesia_cli:auth_clientid_cli(["add", ?CLIENTID, ?PASSWORD]), - [{_, {clientid, ?CLIENTID}, - <>, - _}] = emqx_auth_mnesia_cli:lookup_user({clientid, ?CLIENTID}), - ?assertEqual(Hash, emqx_passwd:hash(HashType, <>)), - - emqx_auth_mnesia_cli:auth_clientid_cli(["update", ?CLIENTID, ?NPASSWORD]), - [{_, {clientid, ?CLIENTID}, - <>, - _}] = emqx_auth_mnesia_cli:lookup_user({clientid, ?CLIENTID}), - ?assertEqual(Hash1, emqx_passwd:hash(HashType, <>)), - - emqx_auth_mnesia_cli:auth_clientid_cli(["del", ?CLIENTID]), - ?assertEqual([], emqx_auth_mnesia_cli:lookup_user(?CLIENTID)), - - emqx_auth_mnesia_cli:auth_clientid_cli(["add", "user1", "pass1"]), - emqx_auth_mnesia_cli:auth_clientid_cli(["add", "user2", "pass2"]), - ?assertEqual(2, length(emqx_auth_mnesia_cli:auth_clientid_cli(["list"]))), - - emqx_auth_mnesia_cli:auth_clientid_cli(usage). - -t_auth_username_cli(_) -> - clean_all_users(), - - HashType = application:get_env(emqx_auth_mnesia, password_hash, sha256), - - emqx_auth_mnesia_cli:auth_username_cli(["add", ?USERNAME, ?PASSWORD]), - [{_, {username, ?USERNAME}, - <>, - _}] = emqx_auth_mnesia_cli:lookup_user({username, ?USERNAME}), - ?assertEqual(Hash, emqx_passwd:hash(HashType, <>)), - - emqx_auth_mnesia_cli:auth_username_cli(["update", ?USERNAME, ?NPASSWORD]), - [{_, {username, ?USERNAME}, - <>, - _}] = emqx_auth_mnesia_cli:lookup_user({username, ?USERNAME}), - ?assertEqual(Hash1, emqx_passwd:hash(HashType, <>)), - - emqx_auth_mnesia_cli:auth_username_cli(["del", ?USERNAME]), - ?assertEqual([], emqx_auth_mnesia_cli:lookup_user(?USERNAME)), - - emqx_auth_mnesia_cli:auth_username_cli(["add", "user1", "pass1"]), - emqx_auth_mnesia_cli:auth_username_cli(["add", "user2", "pass2"]), - ?assertEqual(2, length(emqx_auth_mnesia_cli:auth_username_cli(["list"]))), - - emqx_auth_mnesia_cli:auth_username_cli(usage). - - -t_clientid_rest_api(_Config) -> - clean_all_users(), - - {ok, Result1} = request_http_rest_list(["auth_clientid"]), - [] = get_http_data(Result1), - - Params1 = #{<<"clientid">> => ?CLIENTID, <<"password">> => ?PASSWORD}, - {ok, _} = request_http_rest_add(["auth_clientid"], Params1), - - Path = ["auth_clientid/" ++ binary_to_list(?CLIENTID)], - Params2 = #{<<"clientid">> => ?CLIENTID, <<"password">> => ?NPASSWORD}, - {ok, _} = request_http_rest_update(Path, Params2), - - {ok, Result2} = request_http_rest_lookup(Path), - ?assertMatch(#{<<"clientid">> := ?CLIENTID}, get_http_data(Result2)), - - Params3 = [ #{<<"clientid">> => ?CLIENTID, <<"password">> => ?PASSWORD} - , #{<<"clientid">> => <<"clientid1">>, <<"password">> => ?PASSWORD} - , #{<<"clientid">> => <<"clientid2">>, <<"password">> => ?PASSWORD} - ], - {ok, Result3} = request_http_rest_add(["auth_clientid"], Params3), - ?assertMatch(#{ ?CLIENTID := <<"{error,existed}">> - , <<"clientid1">> := <<"ok">> - , <<"clientid2">> := <<"ok">> - }, get_http_data(Result3)), - - {ok, Result4} = request_http_rest_list(["auth_clientid"]), - ?assertEqual(3, length(get_http_data(Result4))), - - {ok, _} = request_http_rest_delete(Path), - {ok, Result5} = request_http_rest_lookup(Path), - ?assertMatch(#{}, get_http_data(Result5)). - -t_username_rest_api(_Config) -> - clean_all_users(), - - {ok, Result1} = request_http_rest_list(["auth_username"]), - [] = get_http_data(Result1), - - Params1 = #{<<"username">> => ?USERNAME, <<"password">> => ?PASSWORD}, - {ok, _} = request_http_rest_add(["auth_username"], Params1), - - Path = ["auth_username/" ++ binary_to_list(?USERNAME)], - Params2 = #{<<"username">> => ?USERNAME, <<"password">> => ?NPASSWORD}, - {ok, _} = request_http_rest_update(Path, Params2), - - {ok, Result2} = request_http_rest_lookup(Path), - ?assertMatch(#{<<"username">> := ?USERNAME}, get_http_data(Result2)), - - Params3 = [ #{<<"username">> => ?USERNAME, <<"password">> => ?PASSWORD} - , #{<<"username">> => <<"username1">>, <<"password">> => ?PASSWORD} - , #{<<"username">> => <<"username2">>, <<"password">> => ?PASSWORD} - ], - {ok, Result3} = request_http_rest_add(["auth_username"], Params3), - ?assertMatch(#{ ?USERNAME := <<"{error,existed}">> - , <<"username1">> := <<"ok">> - , <<"username2">> := <<"ok">> - }, get_http_data(Result3)), - - {ok, Result4} = request_http_rest_list(["auth_username"]), - ?assertEqual(3, length(get_http_data(Result4))), - - {ok, _} = request_http_rest_delete(Path), - {ok, Result5} = request_http_rest_lookup([Path]), - ?assertMatch(#{}, get_http_data(Result5)). - -t_password_hash(_) -> - clean_all_users(), - {ok, Default} = application:get_env(emqx_auth_mnesia, password_hash), - application:set_env(emqx_auth_mnesia, password_hash, plain), - - %% change the password_hash to 'plain' - application:stop(emqx_auth_mnesia), - ok = application:start(emqx_auth_mnesia), - - Params = #{<<"username">> => ?USERNAME, <<"password">> => ?PASSWORD}, - {ok, _} = request_http_rest_add(["auth_username"], Params), - - %% check - User = #{username => ?USERNAME, - clientid => undefined, - password => ?PASSWORD, - zone => external}, - {ok, #{auth_result := success, - anonymous := false}} = emqx_access_control:authenticate(User), - - application:set_env(emqx_auth_mnesia, password_hash, Default), - application:stop(emqx_auth_mnesia), - ok = application:start(emqx_auth_mnesia). - -%%------------------------------------------------------------------------------ -%% Helpers -%%------------------------------------------------------------------------------ - -clean_all_users() -> - [ mnesia:dirty_delete({emqx_user, Login}) - || Login <- mnesia:dirty_all_keys(emqx_user)]. - -%%-------------------------------------------------------------------- -%% HTTP Request -%%-------------------------------------------------------------------- - -request_http_rest_list(Path) -> - request_api(get, uri(Path), default_auth_header()). - -request_http_rest_lookup(Path) -> - request_api(get, uri([Path]), default_auth_header()). - -request_http_rest_add(Path, Params) -> - request_api(post, uri(Path), [], default_auth_header(), Params). - -request_http_rest_update(Path, Params) -> - request_api(put, uri([Path]), [], default_auth_header(), Params). - -request_http_rest_delete(Login) -> - request_api(delete, uri([Login]), default_auth_header()). - -uri() -> uri([]). -uri(Parts) when is_list(Parts) -> - NParts = [b2l(E) || E <- Parts], - ?HOST ++ filename:join([?BASE_PATH, ?API_VERSION | NParts]). - -%% @private -b2l(B) when is_binary(B) -> - binary_to_list(B); -b2l(L) when is_list(L) -> - L. diff --git a/apps/emqx_auth_mongo/.gitignore b/apps/emqx_auth_mongo/.gitignore deleted file mode 100644 index a6635ffa0..000000000 --- a/apps/emqx_auth_mongo/.gitignore +++ /dev/null @@ -1,24 +0,0 @@ -.eunit -deps -*.o -*.beam -*.plt -erl_crash.dump -ebin -rel/example_project -.concrete/DEV_MODE -.rebar -.DS_Store -.erlang.mk/ -emqx_auth_mongo.d -ct.coverdata -logs/ -test/ct.cover.spec -data/ -cover/ -eunit.coverdata -_build/ -rebar.lock -erlang.mk -etc/emqx_auth_mongo.conf.rendered -.rebar3 diff --git a/apps/emqx_auth_mongo/CHANGES b/apps/emqx_auth_mongo/CHANGES deleted file mode 100644 index 4bddd63a9..000000000 --- a/apps/emqx_auth_mongo/CHANGES +++ /dev/null @@ -1,31 +0,0 @@ - -2.0.7 (2017-01-20) ------------------- - -Tag 2.0.7 - use `cuttlefish:unset()` for commented ACL/super config - -2.0.1 (2016-11-30) ------------------- - -Tag 2.0.1 - -2.0-beta.1 (2016-08-24) ------------------------ - -gen_conf - -1.1.3-beta (2016-08-19) ------------------------ - -Bump version to 1.1.3 - -1.1.2-beta (2016-06-30) ------------------------ - -Bump version to 1.1.2 - -1.1-beta (2016-05-28) ---------------------- - -First public release - diff --git a/apps/emqx_auth_mongo/README.md b/apps/emqx_auth_mongo/README.md deleted file mode 100644 index 3bacfca5b..000000000 --- a/apps/emqx_auth_mongo/README.md +++ /dev/null @@ -1,192 +0,0 @@ -emqx_auth_mongo -=============== - -EMQ X Authentication/ACL with MongoDB - -Build the Plugin ----------------- - -``` -make & make tests -``` - -Configuration -------------- - -File: etc/emqx_auth_mongo.conf - -``` -## MongoDB Topology Type. -## -## Value: single | unknown | sharded | rs -auth.mongo.type = single - -## Sets the set name if type is rs. -## -## Value: String -## auth.mongo.rs_set_name = - -## MongoDB server list. -## -## Value: String -## -## Examples: 127.0.0.1:27017,127.0.0.2:27017... -auth.mongo.server = 127.0.0.1:27017 - -## MongoDB pool size -## -## Value: Number -auth.mongo.pool = 8 - -## MongoDB login user. -## -## Value: String -## auth.mongo.login = - -## MongoDB password. -## -## Value: String -## auth.mongo.password = - -## MongoDB AuthSource -## -## Value: String -## Default: mqtt -## auth.mongo.auth_source = admin - -## MongoDB database -## -## Value: String -auth.mongo.database = mqtt - -## MongoDB write mode. -## -## Value: unsafe | safe -## auth.mongo.w_mode = - -## Mongo read mode. -## -## Value: master | slave_ok -## auth.mongo.r_mode = - -## MongoDB topology options. -auth.mongo.topology.pool_size = 1 -auth.mongo.topology.max_overflow = 0 -## auth.mongo.topology.overflow_ttl = 1000 -## auth.mongo.topology.overflow_check_period = 1000 -## auth.mongo.topology.local_threshold_ms = 1000 -## auth.mongo.topology.connect_timeout_ms = 20000 -## auth.mongo.topology.socket_timeout_ms = 100 -## auth.mongo.topology.server_selection_timeout_ms = 30000 -## auth.mongo.topology.wait_queue_timeout_ms = 1000 -## auth.mongo.topology.heartbeat_frequency_ms = 10000 -## auth.mongo.topology.min_heartbeat_frequency_ms = 1000 - -## Authentication query. -auth.mongo.auth_query.collection = mqtt_user - -auth.mongo.auth_query.password_field = password - -## Password hash. -## -## Value: plain | md5 | sha | sha256 | bcrypt -auth.mongo.auth_query.password_hash = sha256 - -## sha256 with salt suffix -## auth.mongo.auth_query.password_hash = sha256,salt - -## sha256 with salt prefix -## auth.mongo.auth_query.password_hash = salt,sha256 - -## bcrypt with salt prefix -## auth.mongo.auth_query.password_hash = salt,bcrypt - -## pbkdf2 with macfun iterations dklen -## macfun: md4, md5, ripemd160, sha, sha224, sha256, sha384, sha512 -## auth.mongo.auth_query.password_hash = pbkdf2,sha256,1000,20 - -auth.mongo.auth_query.selector = username=%u - -## Enable superuser query. -auth.mongo.super_query = on - -auth.mongo.super_query.collection = mqtt_user - -auth.mongo.super_query.super_field = is_superuser - -auth.mongo.super_query.selector = username=%u - -## Enable ACL query. -auth.mongo.acl_query = on - -auth.mongo.acl_query.collection = mqtt_acl - -auth.mongo.acl_query.selector = username=%u -``` - -Load the Plugin ---------------- - -``` -./bin/emqx_ctl plugins load emqx_auth_mongo -``` - -MongoDB Database ----------------- - -``` -use mqtt -db.createCollection("mqtt_user") -db.createCollection("mqtt_acl") -db.mqtt_user.ensureIndex({"username":1}) -``` - -mqtt_user Collection --------------------- - -``` -{ - username: "user", - password: "password hash", - salt: "password salt", - is_superuser: boolean (true, false), - created: "datetime" -} -``` - -For example: -``` -db.mqtt_user.insert({username: "test", password: "password hash", salt: "password salt", is_superuser: false}) -db.mqtt_user.insert({username: "root", is_superuser: true}) -``` - -mqtt_acl Collection -------------------- - -``` -{ - username: "username", - clientid: "clientid", - publish: ["topic1", "topic2", ...], - subscribe: ["subtop1", "subtop2", ...], - pubsub: ["topic/#", "topic1", ...] -} -``` - -For example: - -``` -db.mqtt_acl.insert({username: "test", publish: ["t/1", "t/2"], subscribe: ["user/%u", "client/%c"]}) -db.mqtt_acl.insert({username: "admin", pubsub: ["#"]}) -``` - -License -------- - -Apache License Version 2.0 - -Author ------- - -EMQ X Team. - diff --git a/apps/emqx_auth_mongo/etc/emqx_auth_mongo.conf b/apps/emqx_auth_mongo/etc/emqx_auth_mongo.conf deleted file mode 100644 index c59c80643..000000000 --- a/apps/emqx_auth_mongo/etc/emqx_auth_mongo.conf +++ /dev/null @@ -1,187 +0,0 @@ -##-------------------------------------------------------------------- -## MongoDB Auth/ACL Plugin -##-------------------------------------------------------------------- - -## MongoDB Topology Type. -## -## Value: single | unknown | sharded | rs -auth.mongo.type = single - -## The set name if type is rs. -## -## Value: String -## auth.mongo.rs_set_name = - -## MongoDB server list. -## -## Value: String -## -## Examples: "127.0.0.1:27017,127.0.0.2:27017,..." -auth.mongo.server = "127.0.0.1:27017" - -## MongoDB pool size -## -## Value: Number -auth.mongo.pool = 8 - -## MongoDB login user. -## -## Value: String -# auth.mongo.username = - -## MongoDB password. -## -## Value: String -## auth.mongo.password = - -## MongoDB AuthSource -## -## Value: String -## Default: mqtt -## auth.mongo.auth_source = admin - -## MongoDB database -## -## Value: String -auth.mongo.database = mqtt - -## MongoDB query timeout -## -## Value: Duration -## auth.mongo.query_timeout = 5s - -## Whether to enable SSL connection. -## -## Value: on | off -## auth.mongo.ssl.enable = off - -## SSL keyfile. -## -## Value: File -## auth.mongo.ssl.keyfile = - -## SSL certfile. -## -## Value: File -## auth.mongo.ssl.certfile = - -## SSL cacertfile. -## -## Value: File -## auth.mongo.ssl.cacertfile = - -## In mode verify_none the default behavior is to allow all x509-path -## validation errors. -## -## Value: true | false -## auth.mongo.ssl.verify = false - -## If not specified, the server's names returned in server's certificate is validated against -## what's provided `auth.mongo.server` config's host part. -## Setting to 'disable' will make EMQ X ignore unmatched server names. -## If set with a host name, the server's names returned in server's certificate is validated -## against this value. -## -## Value: String | disable -## auth.mongo.ssl.server_name_indication = disable - -## MongoDB write mode. -## -## Value: unsafe | safe -## auth.mongo.w_mode = - -## Mongo read mode. -## -## Value: master | slave_ok -## auth.mongo.r_mode = - -## MongoDB topology options. -auth.mongo.topology.pool_size = 1 -auth.mongo.topology.max_overflow = 0 -## auth.mongo.topology.overflow_ttl = 1000 -## auth.mongo.topology.overflow_check_period = 1000 -## auth.mongo.topology.local_threshold_ms = 1000 -## auth.mongo.topology.connect_timeout_ms = 20000 -## auth.mongo.topology.socket_timeout_ms = 100 -## auth.mongo.topology.server_selection_timeout_ms = 30000 -## auth.mongo.topology.wait_queue_timeout_ms = 1000 -## auth.mongo.topology.heartbeat_frequency_ms = 10000 -## auth.mongo.topology.min_heartbeat_frequency_ms = 1000 - -## ------------------------------------------------- -## Auth Query -## ------------------------------------------------- -## Password hash. -## -## Value: plain | md5 | sha | sha256 | bcrypt -auth.mongo.auth_query.password_hash = sha256 - -## sha256 with salt suffix -## auth.mongo.auth_query.password_hash = "sha256,salt" - -## sha256 with salt prefix -## auth.mongo.auth_query.password_hash = "salt,sha256" - -## bcrypt with salt prefix -## auth.mongo.auth_query.password_hash = "salt,bcrypt" - -## pbkdf2 with macfun iterations dklen -## macfun: md4, md5, ripemd160, sha, sha224, sha256, sha384, sha512 -## auth.mongo.auth_query.password_hash = "pbkdf2,sha256,1000,20" - -## Authentication query. -auth.mongo.auth_query.collection = mqtt_user - -## Password mainly fields -## -## Value: password | password,salt -auth.mongo.auth_query.password_field = password - -## Authentication Selector. -## -## Variables: -## - %u: username -## - %c: clientid -## - %C: common name of client TLS cert -## - %d: subject of client TLS cert -## -## auth.mongo.auth_query.selector = {Field}={Placeholder} -auth.mongo.auth_query.selector = "username=%u" - -## ------------------------------------------------- -## Super User Query -## ------------------------------------------------- -auth.mongo.super_query.collection = mqtt_user -auth.mongo.super_query.super_field = is_superuser -#auth.mongo.super_query.selector.1 = username=%u, clientid=%c -auth.mongo.super_query.selector = "username=%u" - -## ACL Selector. -## -## Multiple selectors could be combined with '$or' -## when query acl from mongo. -## -## e.g. -## -## With following 2 selectors configured: -## -## auth.mongo.acl_query.selector.1 = "username=%u" -## auth.mongo.acl_query.selector.2 = "username=$all" -## -## And if a client connected using username 'ilyas', -## then the following mongo command will be used to -## retrieve acl entries: -## -## db.mqtt_acl.find({$or: [{username: "ilyas"}, {username: "$all"}]}); -## -## Variables: -## - %u: username -## - %c: clientid -## -## Examples: -## -## auth.mongo.acl_query.selector.1 = "username=%u,clientid=%c" -## auth.mongo.acl_query.selector.2 = "username=$all" -## auth.mongo.acl_query.selector.3 = "clientid=$all" -auth.mongo.acl_query.collection = mqtt_acl -auth.mongo.acl_query.selector = "username=%u" diff --git a/apps/emqx_auth_mongo/include/emqx_auth_mongo.hrl b/apps/emqx_auth_mongo/include/emqx_auth_mongo.hrl deleted file mode 100644 index 97ecf9973..000000000 --- a/apps/emqx_auth_mongo/include/emqx_auth_mongo.hrl +++ /dev/null @@ -1,37 +0,0 @@ - --define(APP, emqx_auth_mongo). - --define(DEFAULT_SELECTORS, [{<<"username">>, <<"%u">>}]). - --record(superquery, {collection = <<"mqtt_user">>, - field = <<"is_superuser">>, - selector = {<<"username">>, <<"%u">>}}). - --record(authquery, {collection = <<"mqtt_user">>, - field = <<"password">>, - hash = sha256, - selector = {<<"username">>, <<"%u">>}}). - --record(aclquery, {collection = <<"mqtt_acl">>, - selector = {<<"username">>, <<"%u">>}}). - --record(auth_metrics, { - success = 'client.auth.success', - failure = 'client.auth.failure', - ignore = 'client.auth.ignore' - }). - --record(acl_metrics, { - allow = 'client.acl.allow', - deny = 'client.acl.deny', - ignore = 'client.acl.ignore' - }). - --define(METRICS(Type), tl(tuple_to_list(#Type{}))). --define(METRICS(Type, K), #Type{}#Type.K). - --define(AUTH_METRICS, ?METRICS(auth_metrics)). --define(AUTH_METRICS(K), ?METRICS(auth_metrics, K)). - --define(ACL_METRICS, ?METRICS(acl_metrics)). --define(ACL_METRICS(K), ?METRICS(acl_metrics, K)). diff --git a/apps/emqx_auth_mongo/priv/emqx_auth_mongo.schema b/apps/emqx_auth_mongo/priv/emqx_auth_mongo.schema deleted file mode 100644 index cd8c03015..000000000 --- a/apps/emqx_auth_mongo/priv/emqx_auth_mongo.schema +++ /dev/null @@ -1,341 +0,0 @@ -%%-*- mode: erlang -*- -%% emqx_auth_mongo config mapping - -{mapping, "auth.mongo.type", "emqx_auth_mongo.server", [ - {default, single}, - {datatype, {enum, [single, unknown, sharded, rs]}} -]}. - -{mapping, "auth.mongo.rs_set_name", "emqx_auth_mongo.server", [ - {default, "mqtt"}, - {datatype, string} -]}. - -{mapping, "auth.mongo.server", "emqx_auth_mongo.server", [ - {default, "127.0.0.1:27017"}, - {datatype, string} -]}. - -{mapping, "auth.mongo.pool", "emqx_auth_mongo.server", [ - {default, 8}, - {datatype, integer} -]}. - -%% FIXME: compatible with 4.0-4.2 version format, plan to delete in 5.0 -{mapping, "auth.mongo.login", "emqx_auth_mongo.server", [ - {datatype, string} -]}. - -{mapping, "auth.mongo.username", "emqx_auth_mongo.server", [ - {datatype, string} -]}. - -{mapping, "auth.mongo.password", "emqx_auth_mongo.server", [ - {default, ""}, - {datatype, string} -]}. - -{mapping, "auth.mongo.database", "emqx_auth_mongo.server", [ - {default, "mqtt"}, - {datatype, string} -]}. - -{mapping, "auth.mongo.auth_source", "emqx_auth_mongo.server", [ - {default, "mqtt"}, - {datatype, string} -]}. - -{mapping, "auth.mongo.ssl.enable", "emqx_auth_mongo.server", [ - {default, off}, - {datatype, {enum, [on, off, true, false]}} %% FIXME: ture/false is compatible with 4.0-4.2 version format, plan to delete in 5.0 -]}. - -{mapping, "auth.mongo.ssl.keyfile", "emqx_auth_mongo.server", [ - {datatype, string} -]}. - -{mapping, "auth.mongo.ssl.certfile", "emqx_auth_mongo.server", [ - {datatype, string} -]}. - -{mapping, "auth.mongo.ssl.cacertfile", "emqx_auth_mongo.server", [ - {datatype, string} -]}. - -{mapping, "auth.mongo.ssl.verify", "emqx_auth_mongo.server", [ - {default, false}, - {datatype, {enum, [true, false]}} -]}. - -{mapping, "auth.mongo.ssl.server_name_indication", "emqx_auth_mongo.server", [ - {datatype, string} -]}. - -%% FIXME: compatible with 4.0-4.2 version format, plan to delete in 5.0 -{mapping, "auth.mongo.ssl_opts.keyfile", "emqx_auth_mongo.server", [ - {datatype, string} -]}. - -%% FIXME: compatible with 4.0-4.2 version format, plan to delete in 5.0 -{mapping, "auth.mongo.ssl_opts.certfile", "emqx_auth_mongo.server", [ - {datatype, string} -]}. - -%% FIXME: compatible with 4.0-4.2 version format, plan to delete in 5.0 -{mapping, "auth.mongo.ssl_opts.cacertfile", "emqx_auth_mongo.server", [ - {datatype, string} -]}. - -{mapping, "auth.mongo.w_mode", "emqx_auth_mongo.server", [ - {default, undef}, - {datatype, {enum, [safe, unsafe, undef]}} -]}. - -{mapping, "auth.mongo.r_mode", "emqx_auth_mongo.server", [ - {default, undef}, - {datatype, {enum, [master, slave_ok, undef]}} -]}. - -{mapping, "auth.mongo.topology.$name", "emqx_auth_mongo.server", [ - {datatype, integer} -]}. - -{translation, "emqx_auth_mongo.server", fun(Conf) -> - H = cuttlefish:conf_get("auth.mongo.server", Conf), - Hosts = string:tokens(H, ","), - Type0 = cuttlefish:conf_get("auth.mongo.type", Conf), - Pool = cuttlefish:conf_get("auth.mongo.pool", Conf), - %% FIXME: compatible with 4.0-4.2 version format, plan to delete in 5.0 - Login = cuttlefish:conf_get("auth.mongo.username", Conf, - cuttlefish:conf_get("auth.mongo.login", Conf, "") - ), - Passwd = cuttlefish:conf_get("auth.mongo.password", Conf), - DB = cuttlefish:conf_get("auth.mongo.database", Conf), - AuthSrc = cuttlefish:conf_get("auth.mongo.auth_source", Conf), - R = cuttlefish:conf_get("auth.mongo.w_mode", Conf), - W = cuttlefish:conf_get("auth.mongo.r_mode", Conf), - Login0 = case Login =:= [] of - true -> []; - false -> [{login, list_to_binary(Login)}] - end, - Passwd0 = case Passwd =:= [] of - true -> []; - false -> [{password, list_to_binary(Passwd)}] - end, - W0 = case W =:= undef of - true -> []; - false -> [{w_mode, W}] - end, - R0 = case R =:= undef of - true -> []; - false -> [{r_mode, R}] - end, - Filter = fun(Opts) -> [{K, V} || {K, V} <- Opts, V =/= undefined] end, - SslOpts = fun(Prefix) -> - Verify = case cuttlefish:conf_get(Prefix ++ ".verify", Conf, false) of - true -> verify_peer; - false -> verify_none - end, - Filter([{verify, Verify}, - {server_name_indication, case cuttlefish:conf_get(Prefix ++ ".server_name_indication", Conf, undefined) of - "disable" -> disable; - SNI -> SNI - end}, - {keyfile, cuttlefish:conf_get(Prefix ++ ".keyfile", Conf, undefined)}, - {certfile, cuttlefish:conf_get(Prefix ++ ".certfile", Conf, undefined)}, - {cacertfile, cuttlefish:conf_get(Prefix ++ ".cacertfile", Conf, undefined)}]) - end, - - %% FIXME: compatible with 4.0-4.2 version format, plan to delete in 5.0 - GenSsl = case cuttlefish:conf_get("auth.mongo.ssl.cacertfile", Conf, undefined) of - undefined -> [{ssl, true}, {ssl_opts, SslOpts("auth.mongo.ssl_opts")}]; - _ -> [{ssl, true}, {ssl_opts, SslOpts("auth.mongo.ssl")}] - end, - - %% FIXME: compatible with 4.0-4.2 version format, plan to delete in 5.0 - Ssl = case cuttlefish:conf_get("auth.mongo.ssl.enable", Conf) of - on -> GenSsl; - off -> []; - true -> [{ssl, true}, {ssl_opts, SslOpts("auth.mongo.ssl_opts")}]; - false -> [] - end, - - WorkerOptions = [{database, list_to_binary(DB)}, {auth_source, list_to_binary(AuthSrc)}] - ++ Login0 ++ Passwd0 ++ W0 ++ R0 ++ Ssl, - - Vars = cuttlefish_variable:fuzzy_matches(["auth", "mongo", "topology", "$name"], Conf), - Options = lists:map(fun({_, Name}) -> - Name2 = case Name of - "local_threshold_ms" -> "localThresholdMS"; - "connect_timeout_ms" -> "connectTimeoutMS"; - "socket_timeout_ms" -> "socketTimeoutMS"; - "server_selection_timeout_ms" -> "serverSelectionTimeoutMS"; - "wait_queue_timeout_ms" -> "waitQueueTimeoutMS"; - "heartbeat_frequency_ms" -> "heartbeatFrequencyMS"; - "min_heartbeat_frequency_ms" -> "minHeartbeatFrequencyMS"; - _ -> Name - end, - {list_to_atom(Name2), cuttlefish:conf_get("auth.mongo.topology."++Name, Conf)} - end, Vars), - - Type = case Type0 =:= rs of - true -> {Type0, list_to_binary(cuttlefish:conf_get("auth.mongo.rs_set_name", Conf))}; - false -> Type0 - end, - [{type, Type}, - {hosts, Hosts}, - {options, Options}, - {worker_options, WorkerOptions}, - {auto_reconnect, 1}, - {pool_size, Pool}] -end}. - -%% The mongodb operation timeout is specified by the value of `cursor_timeout` from application config, -%% or `infinity` if `cursor_timeout` not specified -{mapping, "auth.mongo.query_timeout", "mongodb.cursor_timeout", [ - {datatype, string} -]}. - -{translation, "mongodb.cursor_timeout", fun(Conf) -> - case cuttlefish:conf_get("auth.mongo.query_timeout", Conf, undefined) of - undefined -> infinity; - Duration -> - case cuttlefish_duration:parse(Duration, ms) of - {error, Reason} -> error(Reason); - Ms when is_integer(Ms) -> Ms - end - end -end}. - -{mapping, "auth.mongo.auth_query.collection", "emqx_auth_mongo.auth_query", [ - {default, "mqtt_user"}, - {datatype, string} -]}. - -{mapping, "auth.mongo.auth_query.password_field", "emqx_auth_mongo.auth_query", [ - {default, "password"}, - {datatype, string} -]}. - -{mapping, "auth.mongo.auth_query.password_hash", "emqx_auth_mongo.auth_query", [ - {datatype, string} -]}. - -{mapping, "auth.mongo.auth_query.selector", "emqx_auth_mongo.auth_query", [ - {default, ""}, - {datatype, string} -]}. - -{translation, "emqx_auth_mongo.auth_query", fun(Conf) -> - case cuttlefish:conf_get("auth.mongo.auth_query.collection", Conf) of - undefined -> cuttlefish:unset(); - Collection -> - PasswordField = cuttlefish:conf_get("auth.mongo.auth_query.password_field", Conf), - PasswordHash = cuttlefish:conf_get("auth.mongo.auth_query.password_hash", Conf), - SelectorStr = cuttlefish:conf_get("auth.mongo.auth_query.selector", Conf), - SelectorList = - lists:map(fun(Selector) -> - case string:tokens(Selector, "=") of - [Field, Val] -> {list_to_binary(Field), list_to_binary(Val)}; - _ -> {<<"username">>, <<"%u">>} - end - end, string:tokens(SelectorStr, ", ")), - - PasswordFields = [list_to_binary(Field) || Field <- string:tokens(PasswordField, ",")], - HashValue = - case string:tokens(PasswordHash, ",") of - [Hash] -> list_to_atom(Hash); - [Prefix, Suffix] -> {list_to_atom(Prefix), list_to_atom(Suffix)}; - [Hash, MacFun, Iterations, Dklen] -> {list_to_atom(Hash), list_to_atom(MacFun), list_to_integer(Iterations), list_to_integer(Dklen)}; - _ -> plain - end, - [{collection, Collection}, - {password_field, PasswordFields}, - {password_hash, HashValue}, - {selector, SelectorList}] - end -end}. - -{mapping, "auth.mongo.super_query", "emqx_auth_mongo.super_query", [ - {default, off}, - {datatype, flag} -]}. - -{mapping, "auth.mongo.super_query.collection", "emqx_auth_mongo.super_query", [ - {default, "mqtt_user"}, - {datatype, string} -]}. - -{mapping, "auth.mongo.super_query.super_field", "emqx_auth_mongo.super_query", [ - {default, "is_superuser"}, - {datatype, string} -]}. - -{mapping, "auth.mongo.super_query.selector", "emqx_auth_mongo.super_query", [ - {default, ""}, - {datatype, string} -]}. - -{translation, "emqx_auth_mongo.super_query", fun(Conf) -> - case cuttlefish:conf_get("auth.mongo.super_query.collection", Conf) of - undefined -> cuttlefish:unset(); - Collection -> - SuperField = cuttlefish:conf_get("auth.mongo.super_query.super_field", Conf), - SelectorStr = cuttlefish:conf_get("auth.mongo.super_query.selector", Conf), - SelectorList = - lists:map(fun(Selector) -> - case string:tokens(Selector, "=") of - [Field, Val] -> {list_to_binary(Field), list_to_binary(Val)}; - _ -> {<<"username">>, <<"%u">>} - end - end, string:tokens(SelectorStr, ", ")), - [{collection, Collection}, {super_field, SuperField}, {selector, SelectorList}] - end -end}. - -{mapping, "auth.mongo.acl_query", "emqx_auth_mongo.acl_query", [ - {default, off}, - {datatype, flag} -]}. - -{mapping, "auth.mongo.acl_query.collection", "emqx_auth_mongo.acl_query", [ - {default, "mqtt_user"}, - {datatype, string} -]}. - -{mapping, "auth.mongo.acl_query.selector", "emqx_auth_mongo.acl_query", [ - {default, ""}, - {datatype, string} -]}. -{mapping, "auth.mongo.acl_query.selector.$id", "emqx_auth_mongo.acl_query", [ - {default, ""}, - {datatype, string} -]}. - -{translation, "emqx_auth_mongo.acl_query", fun(Conf) -> - case cuttlefish:conf_get("auth.mongo.acl_query.collection", Conf) of - undefined -> cuttlefish:unset(); - Collection -> - SelectorStrList = - lists:map( - fun - ({["auth","mongo","acl_query","selector"], ConfEntry}) -> - ConfEntry; - ({["auth","mongo","acl_query","selector", _], ConfEntry}) -> - ConfEntry - end, - cuttlefish_variable:filter_by_prefix("auth.mongo.acl_query.selector", Conf)), - SelectorListList = - lists:map( - fun(SelectorStr) -> - lists:map(fun(Selector) -> - case string:tokens(Selector, "=") of - [Field, Val] -> {list_to_binary(Field), list_to_binary(Val)}; - _ -> {<<"username">>, <<"%u">>} - end - end, string:tokens(SelectorStr, ", ")) - end, - SelectorStrList), - [{collection, Collection}, {selector, SelectorListList}] - end -end}. diff --git a/apps/emqx_auth_mongo/rebar.config b/apps/emqx_auth_mongo/rebar.config deleted file mode 100644 index f44e69543..000000000 --- a/apps/emqx_auth_mongo/rebar.config +++ /dev/null @@ -1,32 +0,0 @@ -{deps, - %% NOTE: mind poolboy version when updating mongodb-erlang version - [{mongodb, {git,"https://github.com/emqx/mongodb-erlang", {tag, "v3.0.7"}}}, - %% mongodb-erlang uses a special fork https://github.com/comtihon/poolboy.git - %% (which has overflow_ttl feature added). - %% However, it references `{branch, "master}` (commit 9c06a9a on 2021-04-07). - %% By accident, We have always been using the upstream fork due to - %% eredis_cluster's dependency getting resolved earlier. - %% Here we pin 1.5.2 to avoid surprises in the future. - {poolboy, {git, "https://github.com/emqx/poolboy.git", {tag, "1.5.2"}}} - ]}. - -{edoc_opts, [{preprocess, true}]}. -{erl_opts, [warn_unused_vars, - warn_shadow_vars, - warn_unused_import, - warn_obsolete_guard, - debug_info, - compressed, - {parse_transform} - ]}. -{overrides, [{add, [{erl_opts, [compressed]}]}]}. - -{xref_checks, [undefined_function_calls, undefined_functions, - locals_not_used, deprecated_function_calls, - warnings_as_errors, deprecated_functions - ]}. - -{cover_enabled, true}. -{cover_opts, [verbose]}. -{cover_export_enabled, true}. - diff --git a/apps/emqx_auth_mongo/src/emqx_acl_mongo.erl b/apps/emqx_auth_mongo/src/emqx_acl_mongo.erl deleted file mode 100644 index 653600395..000000000 --- a/apps/emqx_auth_mongo/src/emqx_acl_mongo.erl +++ /dev/null @@ -1,91 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_acl_mongo). - --include("emqx_auth_mongo.hrl"). --include_lib("emqx/include/emqx.hrl"). --include_lib("emqx/include/logger.hrl"). - -%% ACL callbacks --export([ register_metrics/0 - , check_acl/5 - , description/0 - ]). --spec(register_metrics() -> ok). -register_metrics() -> - lists:foreach(fun emqx_metrics:ensure/1, ?ACL_METRICS). - -check_acl(#{username := <<$$, _/binary>>}, _PubSub, _Topic, _AclResult, _State) -> - ok; - -check_acl(ClientInfo, PubSub, Topic, _AclResult, Env = #{aclquery := AclQuery}) -> - #aclquery{collection = Coll, selector = SelectorList} = AclQuery, - Pool = maps:get(pool, Env, ?APP), - SelectorMapList = - lists:map(fun(Selector) -> - maps:from_list(emqx_auth_mongo:replvars(Selector, ClientInfo)) - end, SelectorList), - case emqx_auth_mongo:query_multi(Pool, Coll, SelectorMapList) of - [] -> ok; - Rows -> - try match(ClientInfo, Topic, topics(PubSub, Rows)) of - matched -> emqx_metrics:inc(?ACL_METRICS(allow)), - {stop, allow}; - nomatch -> emqx_metrics:inc(?ACL_METRICS(deny)), - {stop, deny} - catch - _Err:Reason-> - ?LOG(error, "[MongoDB] Check mongo ~p ACL failed, got ACL config: ~p, error: :~p", - [PubSub, Rows, Reason]), - emqx_metrics:inc(?ACL_METRICS(ignore)), - ignore - end - end. - - -match(_ClientInfo, _Topic, []) -> - nomatch; -match(ClientInfo, Topic, [TopicFilter|More]) -> - case emqx_topic:match(Topic, feedvar(ClientInfo, TopicFilter)) of - true -> matched; - false -> match(ClientInfo, Topic, More) - end. - -topics(publish, Rows) -> - lists:foldl(fun(Row, Acc) -> - Topics = maps:get(<<"publish">>, Row, []) ++ maps:get(<<"pubsub">>, Row, []), - lists:umerge(Acc, Topics) - end, [], Rows); - -topics(subscribe, Rows) -> - lists:foldl(fun(Row, Acc) -> - Topics = maps:get(<<"subscribe">>, Row, []) ++ maps:get(<<"pubsub">>, Row, []), - lists:umerge(Acc, Topics) - end, [], Rows). - -feedvar(#{clientid := ClientId, username := Username}, Str) -> - lists:foldl(fun({Var, Val}, Acc) -> - feedvar(Acc, Var, Val) - end, Str, [{"%u", Username}, {"%c", ClientId}]). - -feedvar(Str, _Var, undefined) -> - Str; -feedvar(Str, Var, Val) -> - re:replace(Str, Var, Val, [global, {return, binary}]). - -description() -> "ACL with MongoDB". - diff --git a/apps/emqx_auth_mongo/src/emqx_auth_mongo.app.src b/apps/emqx_auth_mongo/src/emqx_auth_mongo.app.src deleted file mode 100644 index ab0b4ff56..000000000 --- a/apps/emqx_auth_mongo/src/emqx_auth_mongo.app.src +++ /dev/null @@ -1,14 +0,0 @@ -{application, emqx_auth_mongo, - [{description, "EMQ X Authentication/ACL with MongoDB"}, - {vsn, "4.4.0"}, % strict semver, bump manually! - {modules, []}, - {registered, [emqx_auth_mongo_sup]}, - {applications, [kernel,stdlib,mongodb,ecpool]}, - {mod, {emqx_auth_mongo_app,[]}}, - {env, []}, - {licenses, ["Apache-2.0"]}, - {maintainers, ["EMQ X Team "]}, - {links, [{"Homepage", "https://emqx.io/"}, - {"Github", "https://github.com/emqx/emqx-auth-mongo"} - ]} - ]}. diff --git a/apps/emqx_auth_mongo/src/emqx_auth_mongo.erl b/apps/emqx_auth_mongo/src/emqx_auth_mongo.erl deleted file mode 100644 index cd1d21b42..000000000 --- a/apps/emqx_auth_mongo/src/emqx_auth_mongo.erl +++ /dev/null @@ -1,138 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_auth_mongo). - --behaviour(ecpool_worker). - --include("emqx_auth_mongo.hrl"). --include_lib("emqx/include/emqx.hrl"). --include_lib("emqx/include/logger.hrl"). --include_lib("emqx/include/types.hrl"). - --export([ register_metrics/0 - , check/3 - , description/0 - ]). - --export([ replvar/2 - , replvars/2 - , connect/1 - , query/3 - , query_multi/3 - ]). - --spec(register_metrics() -> ok). -register_metrics() -> - lists:foreach(fun emqx_metrics:ensure/1, ?AUTH_METRICS). - -check(ClientInfo = #{password := Password}, AuthResult, - Env = #{authquery := AuthQuery, superquery := SuperQuery}) -> - #authquery{collection = Collection, field = Fields, - hash = HashType, selector = Selector} = AuthQuery, - Pool = maps:get(pool, Env, ?APP), - case query(Pool, Collection, maps:from_list(replvars(Selector, ClientInfo))) of - undefined -> emqx_metrics:inc(?AUTH_METRICS(ignore)); - {error, Reason} -> - ?LOG(error, "[MongoDB] Can't connect to MongoDB server: ~0p", [Reason]), - ok = emqx_metrics:inc(?AUTH_METRICS(failure)), - {stop, AuthResult#{auth_result => not_authorized, anonymous => false}}; - UserMap -> - Result = case [maps:get(Field, UserMap, undefined) || Field <- Fields] of - [undefined] -> {error, password_error}; - [PassHash] -> - check_pass({PassHash, Password}, HashType); - [PassHash, Salt|_] -> - check_pass({PassHash, Salt, Password}, HashType) - end, - case Result of - ok -> - ok = emqx_metrics:inc(?AUTH_METRICS(success)), - {stop, AuthResult#{is_superuser => is_superuser(Pool, SuperQuery, ClientInfo), - anonymous => false, - auth_result => success}}; - {error, Error} -> - ?LOG(error, "[MongoDB] check auth fail: ~p", [Error]), - ok = emqx_metrics:inc(?AUTH_METRICS(failure)), - {stop, AuthResult#{auth_result => Error, anonymous => false}} - end - end. - -check_pass(Password, HashType) -> - case emqx_passwd:check_pass(Password, HashType) of - ok -> ok; - {error, _Reason} -> {error, not_authorized} - end. - -description() -> "Authentication with MongoDB". - -%%-------------------------------------------------------------------- -%% Is Superuser? -%%-------------------------------------------------------------------- -is_superuser(_Pool, undefined, _ClientInfo) -> - false; -is_superuser(Pool, #superquery{collection = Coll, field = Field, selector = Selector}, ClientInfo) -> - case query(Pool, Coll, maps:from_list(replvars(Selector, ClientInfo))) of - undefined -> false; - {error, Reason} -> - ?LOG(error, "[MongoDB] Can't connect to MongoDB server: ~0p", [Reason]), - false; - Row -> - case maps:get(Field, Row, false) of - true -> true; - _False -> false - end - end. - -replvars(VarList, ClientInfo) -> - lists:map(fun(Var) -> replvar(Var, ClientInfo) end, VarList). - -replvar({Field, <<"%u">>}, #{username := Username}) -> - {Field, Username}; -replvar({Field, <<"%c">>}, #{clientid := ClientId}) -> - {Field, ClientId}; -replvar({Field, <<"%C">>}, #{cn := CN}) -> - {Field, CN}; -replvar({Field, <<"%d">>}, #{dn := DN}) -> - {Field, DN}; -replvar(Selector, _ClientInfo) -> - Selector. - -%%-------------------------------------------------------------------- -%% MongoDB Connect/Query -%%-------------------------------------------------------------------- - -connect(Opts) -> - Type = proplists:get_value(type, Opts, single), - Hosts = proplists:get_value(hosts, Opts, []), - Options = proplists:get_value(options, Opts, []), - WorkerOptions = proplists:get_value(worker_options, Opts, []), - mongo_api:connect(Type, Hosts, Options, WorkerOptions). - -query(Pool, Collection, Selector) -> - ecpool:with_client(Pool, fun(Conn) -> mongo_api:find_one(Conn, Collection, Selector, #{}) end). - -query_multi(Pool, Collection, SelectorList) -> - lists:reverse(lists:flatten(lists:foldl(fun(Selector, Acc1) -> - Batch = ecpool:with_client(Pool, fun(Conn) -> - case mongo_api:find(Conn, Collection, Selector, #{}) of - [] -> []; - {ok, Cursor} -> - mc_cursor:foldl(fun(O, Acc2) -> [O|Acc2] end, [], Cursor, 1000) - end - end), - [Batch|Acc1] - end, [], SelectorList))). diff --git a/apps/emqx_auth_mongo/src/emqx_auth_mongo_app.erl b/apps/emqx_auth_mongo/src/emqx_auth_mongo_app.erl deleted file mode 100644 index b8d9431c2..000000000 --- a/apps/emqx_auth_mongo/src/emqx_auth_mongo_app.erl +++ /dev/null @@ -1,88 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_auth_mongo_app). - --behaviour(application). - --emqx_plugin(auth). - --include("emqx_auth_mongo.hrl"). - --import(proplists, [get_value/3]). - -%% Application callbacks --export([ start/2 - , prep_stop/1 - , stop/1 - ]). - -%%-------------------------------------------------------------------- -%% Application callbacks -%%-------------------------------------------------------------------- - -start(_StartType, _StartArgs) -> - {ok, Sup} = emqx_auth_mongo_sup:start_link(), - with_env(auth_query, fun reg_authmod/1), - with_env(acl_query, fun reg_aclmod/1), - {ok, Sup}. - -prep_stop(State) -> - ok = emqx:unhook('client.authenticate', {emqx_auth_mongo, check}), - ok = emqx:unhook('client.check_acl', {emqx_acl_mongo, check_acl}), - State. - -stop(_State) -> - ok. - -reg_authmod(AuthQuery) -> - emqx_auth_mongo:register_metrics(), - SuperQuery = r(super_query, application:get_env(?APP, super_query, undefined)), - ok = emqx:hook('client.authenticate', {emqx_auth_mongo, check, - [#{authquery => AuthQuery, superquery => SuperQuery, pool => ?APP}] - }). - -reg_aclmod(AclQuery) -> - emqx_acl_mongo:register_metrics(), - ok = emqx:hook('client.check_acl', {emqx_acl_mongo, check_acl, [#{aclquery => AclQuery, pool => ?APP}]}). - -%%-------------------------------------------------------------------- -%% Internal functions -%%-------------------------------------------------------------------- - -with_env(Name, Fun) -> - case application:get_env(?APP, Name) of - undefined -> ok; - {ok, Config} -> Fun(r(Name, Config)) - end. - -r(super_query, undefined) -> - undefined; -r(super_query, Config) -> - #superquery{collection = list_to_binary(get_value(collection, Config, "mqtt_user")), - field = list_to_binary(get_value(super_field, Config, "is_superuser")), - selector = get_value(selector, Config, ?DEFAULT_SELECTORS)}; - -r(auth_query, Config) -> - #authquery{collection = list_to_binary(get_value(collection, Config, "mqtt_user")), - field = get_value(password_field, Config, [<<"password">>]), - hash = get_value(password_hash, Config, sha256), - selector = get_value(selector, Config, ?DEFAULT_SELECTORS)}; - -r(acl_query, Config) -> - #aclquery{collection = list_to_binary(get_value(collection, Config, "mqtt_acl")), - selector = get_value(selector, Config, [?DEFAULT_SELECTORS])}. - diff --git a/apps/emqx_auth_mongo/src/emqx_auth_mongo_sup.erl b/apps/emqx_auth_mongo/src/emqx_auth_mongo_sup.erl deleted file mode 100644 index 3f27cb1dd..000000000 --- a/apps/emqx_auth_mongo/src/emqx_auth_mongo_sup.erl +++ /dev/null @@ -1,34 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_auth_mongo_sup). - --behaviour(supervisor). - --include("emqx_auth_mongo.hrl"). - --export([start_link/0]). - --export([init/1]). - -start_link() -> - supervisor:start_link({local, ?MODULE}, ?MODULE, []). - -init([]) -> - {ok, PoolEnv} = application:get_env(?APP, server), - PoolSpec = ecpool:pool_spec(?APP, ?APP, ?APP, PoolEnv), - {ok, {{one_for_all, 10, 100}, [PoolSpec]}}. - diff --git a/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE.erl b/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE.erl deleted file mode 100644 index a988e87bb..000000000 --- a/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE.erl +++ /dev/null @@ -1,169 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_auth_mongo_SUITE). - --compile(export_all). --compile(nowarn_export_all). - --include_lib("emqx/include/emqx.hrl"). --include_lib("common_test/include/ct.hrl"). --include_lib("eunit/include/eunit.hrl"). - --define(APP, emqx_auth_mongo). - --define(POOL(App), ecpool_worker:client(gproc_pool:pick_worker({ecpool, App}))). - --define(MONGO_CL_ACL, <<"mqtt_acl">>). --define(MONGO_CL_USER, <<"mqtt_user">>). - --define(INIT_ACL, [{<<"username">>, <<"testuser">>, <<"clientid">>, <<"null">>, <<"subscribe">>, [<<"#">>]}, - {<<"username">>, <<"dashboard">>, <<"clientid">>, <<"null">>, <<"pubsub">>, [<<"$SYS/#">>]}, - {<<"username">>, <<"user3">>, <<"clientid">>, <<"null">>, <<"publish">>, [<<"a/b/c">>]}]). - --define(INIT_AUTH, [{<<"username">>, <<"plain">>, <<"password">>, <<"plain">>, <<"salt">>, <<"salt">>, <<"is_superuser">>, true}, - {<<"username">>, <<"md5">>, <<"password">>, <<"1bc29b36f623ba82aaf6724fd3b16718">>, <<"salt">>, <<"salt">>, <<"is_superuser">>, false}, - {<<"username">>, <<"sha">>, <<"password">>, <<"d8f4590320e1343a915b6394170650a8f35d6926">>, <<"salt">>, <<"salt">>, <<"is_superuser">>, false}, - {<<"username">>, <<"sha256">>, <<"password">>, <<"5d5b09f6dcb2d53a5fffc60c4ac0d55fabdf556069d6631545f42aa6e3500f2e">>, <<"salt">>, <<"salt">>, <<"is_superuser">>, false}, - {<<"username">>, <<"pbkdf2_password">>, <<"password">>, <<"cdedb5281bb2f801565a1122b2563515">>, <<"salt">>, <<"ATHENA.MIT.EDUraeburn">>, <<"is_superuser">>, false}, - {<<"username">>, <<"bcrypt_foo">>, <<"password">>, <<"$2a$12$sSS8Eg.ovVzaHzi1nUHYK.HbUIOdlQI0iS22Q5rd5z.JVVYH6sfm6">>, <<"salt">>, <<"$2a$12$sSS8Eg.ovVzaHzi1nUHYK.">>, <<"is_superuser">>, false} - ]). - -%%-------------------------------------------------------------------- -%% Setups -%%-------------------------------------------------------------------- - -all() -> - emqx_ct:all(?MODULE). - -init_per_suite(Cfg) -> - emqx_ct_helpers:start_apps([emqx_auth_mongo], fun set_special_confs/1), - init_mongo_data(), - Cfg. - -end_per_suite(_Cfg) -> - deinit_mongo_data(), - emqx_ct_helpers:stop_apps([emqx_auth_mongo]). - -set_special_confs(emqx) -> - application:set_env(emqx, acl_nomatch, deny), - application:set_env(emqx, allow_anonymous, false), - application:set_env(emqx, enable_acl_cache, false); -set_special_confs(_App) -> - ok. - -init_mongo_data() -> - %% Users - {ok, Connection} = ?POOL(?APP), - mongo_api:delete(Connection, ?MONGO_CL_USER, {}), - ?assertMatch({{true, _}, _}, mongo_api:insert(Connection, ?MONGO_CL_USER, ?INIT_AUTH)), - %% ACLs - mongo_api:delete(Connection, ?MONGO_CL_ACL, {}), - ?assertMatch({{true, _}, _}, mongo_api:insert(Connection, ?MONGO_CL_ACL, ?INIT_ACL)). - -deinit_mongo_data() -> - {ok, Connection} = ?POOL(?APP), - mongo_api:delete(Connection, ?MONGO_CL_USER, {}), - mongo_api:delete(Connection, ?MONGO_CL_ACL, {}). - -%%-------------------------------------------------------------------- -%% Test cases -%%-------------------------------------------------------------------- - -t_check_auth(_) -> - Plain = #{zone => external, clientid => <<"client1">>, username => <<"plain">>}, - Plain1 = #{zone => external, clientid => <<"client1">>, username => <<"plain2">>}, - Md5 = #{zone => external, clientid => <<"md5">>, username => <<"md5">>}, - Sha = #{zone => external, clientid => <<"sha">>, username => <<"sha">>}, - Sha256 = #{zone => external, clientid => <<"sha256">>, username => <<"sha256">>}, - Pbkdf2 = #{zone => external, clientid => <<"pbkdf2_password">>, username => <<"pbkdf2_password">>}, - Bcrypt = #{zone => external, clientid => <<"bcrypt_foo">>, username => <<"bcrypt_foo">>}, - User1 = #{zone => external, clientid => <<"bcrypt_foo">>, username => <<"user">>}, - reload({auth_query, [{password_hash, plain}]}), - %% With exactly username/password, connection success - {ok, #{is_superuser := true}} = emqx_access_control:authenticate(Plain#{password => <<"plain">>}), - %% With exactly username and wrong password, connection fail - {error, _} = emqx_access_control:authenticate(Plain#{password => <<"error_pwd">>}), - %% With wrong username and wrong password, emqx_auth_mongo auth fail, then allow anonymous authentication - {error, _} = emqx_access_control:authenticate(Plain1#{password => <<"error_pwd">>}), - %% With wrong username and exactly password, emqx_auth_mongo auth fail, then allow anonymous authentication - {error, _} = emqx_access_control:authenticate(Plain1#{password => <<"plain">>}), - reload({auth_query, [{password_hash, md5}]}), - {ok, #{is_superuser := false}} = emqx_access_control:authenticate(Md5#{password => <<"md5">>}), - reload({auth_query, [{password_hash, sha}]}), - {ok, #{is_superuser := false}} = emqx_access_control:authenticate(Sha#{password => <<"sha">>}), - reload({auth_query, [{password_hash, sha256}]}), - {ok, #{is_superuser := false}} = emqx_access_control:authenticate(Sha256#{password => <<"sha256">>}), - %%pbkdf2 sha - reload({auth_query, [{password_hash, {pbkdf2, sha, 1, 16}}, {password_field, [<<"password">>, <<"salt">>]}]}), - {ok, #{is_superuser := false}} = emqx_access_control:authenticate(Pbkdf2#{password => <<"password">>}), - reload({auth_query, [{password_hash, {salt, bcrypt}}]}), - {ok, #{is_superuser := false}} = emqx_access_control:authenticate(Bcrypt#{password => <<"foo">>}), - {error, _} = emqx_access_control:authenticate(User1#{password => <<"foo">>}). - -t_check_acl(_) -> - {ok, Connection} = ?POOL(?APP), - User1 = #{zone => external, clientid => <<"client1">>, username => <<"testuser">>}, - User2 = #{zone => external, clientid => <<"client2">>, username => <<"dashboard">>}, - User3 = #{zone => external, clientid => <<"client2">>, username => <<"user3">>}, - User4 = #{zone => external, clientid => <<"$$client2">>, username => <<"$$user3">>}, - 3 = mongo_api:count(Connection, ?MONGO_CL_ACL, {}, 17), - %% ct log output - allow = emqx_access_control:check_acl(User1, subscribe, <<"users/testuser/1">>), - deny = emqx_access_control:check_acl(User1, subscribe, <<"$SYS/testuser/1">>), - deny = emqx_access_control:check_acl(User2, subscribe, <<"a/b/c">>), - allow = emqx_access_control:check_acl(User2, subscribe, <<"$SYS/testuser/1">>), - allow = emqx_access_control:check_acl(User3, publish, <<"a/b/c">>), - deny = emqx_access_control:check_acl(User3, publish, <<"c">>), - deny = emqx_access_control:check_acl(User4, publish, <<"a/b/c">>). - -t_acl_super(_) -> - reload({auth_query, [{password_hash, plain}, {password_field, [<<"password">>]}]}), - {ok, C} = emqtt:start_link([{clientid, <<"simpleClient">>}, - {username, <<"plain">>}, - {password, <<"plain">>}]), - {ok, _} = emqtt:connect(C), - timer:sleep(10), - emqtt:subscribe(C, <<"TopicA">>, qos2), - timer:sleep(1000), - emqtt:publish(C, <<"TopicA">>, <<"Payload">>, qos2), - timer:sleep(1000), - receive - {publish, #{payload := Payload}} -> - ?assertEqual(<<"Payload">>, Payload) - after - 1000 -> - ct:fail({receive_timeout, <<"Payload">>}), - ok - end, - emqtt:disconnect(C). - -%%-------------------------------------------------------------------- -%% Utils -%%-------------------------------------------------------------------- - -reload({Par, Vals}) when is_list(Vals) -> - application:stop(?APP), - {ok, TupleVals} = application:get_env(?APP, Par), - NewVals = - lists:filtermap(fun({K, V}) -> - case lists:keymember(K, 1, Vals) of - false ->{true, {K, V}}; - _ -> false - end - end, TupleVals), - application:set_env(?APP, Par, lists:append(NewVals, Vals)), - application:start(?APP). diff --git a/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/ca-key.pem b/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/ca-key.pem deleted file mode 100644 index e9717011e..000000000 --- a/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/ca-key.pem +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEpAIBAAKCAQEA0kGUBi9NDp65jgdxKfizIfuSr2wpwb44yM9SuP4oUQSULOA2 -4iFpLR/c5FAYHU81y9Vx91dQjdZfffaBZuv2zVvteXUkol8Nez7boKbo2E41MTew -8edtNKZAQVvnaHAC2NCZxjchCzUCDEoUUcl+cIERZ8R48FBqK5iTVcMRIx1akwus -+dhBqP0ykA5TGOWZkJrLM9aUXSPQha9+wXlOpkvu0Ur2nkX8PPJnifWao9UShSar -ll1IqPZNCSlZMwcFYcQNBCpdvITUUYlHvMRQV64bUpOxUGDuJkQL3dLKBlNuBRlJ -BcjBAKw7rFnwwHZcMmQ9tan/dZzpzwjo/T0XjwIDAQABAoIBAQCSHvUqnzDkWjcG -l/Fzg92qXlYBCCC0/ugj1sHcwvVt6Mq5rVE3MpUPwTcYjPlVVTlD4aEEjm/zQuq2 -ddxUlOS+r4aIhHrjRT/vSS4FpjnoKeIZxGR6maVxk6DQS3i1QjMYT1CvSpzyVvKH -a+xXMrtmoKxh+085ZAmFJtIuJhUA2yEa4zggCxWnvz8ecLClUPfVDPhdLBHc3KmL -CRpHEC6L/wanvDPRdkkzfKyaJuIJlTDaCg63AY5sDkTW2I57iI/nJ3haSeidfQKz -39EfbnM1A/YprIakafjAu3frBIsjBVcxwGihZmL/YriTHjOggJF841kT5zFkkv2L -/530Wk6xAoGBAOqZLZ4DIi/zLndEOz1mRbUfjc7GQUdYplBnBwJ22VdS0P4TOXnd -UbJth2MA92NM7ocTYVFl4TVIZY/Y+Prxk7KQdHWzR7JPpKfx9OEVgtSqV0vF9eGI -rKp79Y1T4Mvc3UcQCXX6TP7nHLihEzpS8odm2LW4txrOiLsn4Fq/IWrLAoGBAOVv -6U4tm3lImotUupKLZPKEBYwruo9qRysoug9FiorP4TjaBVOfltiiHbAQD6aGfVtN -SZpZZtrs17wL7Xl4db5asgMcZd+8Hkfo5siR7AuGW9FZloOjDcXb5wCh9EvjJ74J -Cjw7RqyVymq9t7IP6wnVwj5Ck48YhlOZCz/mzlnNAoGAWq7NYFgLvgc9feLFF23S -IjpJQZWHJEITP98jaYNxbfzYRm49+GphqxwFinKULjFNvq7yHlnIXSVYBOu1CqOZ -GRwXuGuNmlKI7lZr9xmukfAqgGLMMdr4C4qRF4lFyufcLRz42z7exmWlx4ST/yaT -E13hBRWayeTuG5JFei6Jh1MCgYEAqmX4LyC+JFBgvvQZcLboLRkSCa18bADxhENG -FAuAvmFvksqRRC71WETmqZj0Fqgxt7pp3KFjO1rFSprNLvbg85PmO1s+6fCLyLpX -lESTu2d5D71qhK93jigooxalGitFm+SY3mzjq0/AOpBWOn+J/w7rqVPGxXLgaHv0 -l+vx+00CgYBOvo9/ImjwYii2jFl+sHEoCzlvpITi2temRlT2j6ulSjCLJgjwEFw9 -8e+vvfQumQOsutakUVyURrkMGNDiNlIv8kv5YLCCkrwN22E6Ghyi69MJUvHQXkc/ -QZhjn/luyfpB5f/BeHFS2bkkxAXo+cfG45ApY3Qfz6/7o+H+vDa6/A== ------END RSA PRIVATE KEY----- diff --git a/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/ca.pem b/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/ca.pem deleted file mode 100644 index 00b31d8a4..000000000 --- a/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/ca.pem +++ /dev/null @@ -1,19 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDAzCCAeugAwIBAgIBATANBgkqhkiG9w0BAQsFADA8MTowOAYDVQQDDDFNeVNR -TF9TZXJ2ZXJfOC4wLjE5X0F1dG9fR2VuZXJhdGVkX0NBX0NlcnRpZmljYXRlMB4X -DTIwMDYxMTAzMzg0NloXDTMwMDYwOTAzMzg0NlowPDE6MDgGA1UEAwwxTXlTUUxf -U2VydmVyXzguMC4xOV9BdXRvX0dlbmVyYXRlZF9DQV9DZXJ0aWZpY2F0ZTCCASIw -DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANJBlAYvTQ6euY4HcSn4syH7kq9s -KcG+OMjPUrj+KFEElCzgNuIhaS0f3ORQGB1PNcvVcfdXUI3WX332gWbr9s1b7Xl1 -JKJfDXs+26Cm6NhONTE3sPHnbTSmQEFb52hwAtjQmcY3IQs1AgxKFFHJfnCBEWfE -ePBQaiuYk1XDESMdWpMLrPnYQaj9MpAOUxjlmZCayzPWlF0j0IWvfsF5TqZL7tFK -9p5F/DzyZ4n1mqPVEoUmq5ZdSKj2TQkpWTMHBWHEDQQqXbyE1FGJR7zEUFeuG1KT -sVBg7iZEC93SygZTbgUZSQXIwQCsO6xZ8MB2XDJkPbWp/3Wc6c8I6P09F48CAwEA -AaMQMA4wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEADKz6bIpP5anp -GgLB0jkclRWuMlS4qqIt4itSsMXPJ/ezpHwECixmgW2TIQl6S1woRkUeMxhT2/Ay -Sn/7aKxuzRagyE5NEGOvrOuAP5RO2ZdNJ/X3/Rh533fK1sOTEEbSsWUvW6iSkZef -rsfZBVP32xBhRWkKRdLeLB4W99ADMa0IrTmZPCXHSSE2V4e1o6zWLXcOZeH1Qh8N -SkelBweR+8r1Fbvy1r3s7eH7DCbYoGEDVLQGOLvzHKBisQHmoDnnF5E9g1eeNRdg -o+vhOKfYCOzeNREJIqS42PHcGhdNRk90ycigPmfUJclz1mDHoMjKR2S5oosTpr65 -tNPx3CL7GA== ------END CERTIFICATE----- diff --git a/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/client-cert.pem b/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/client-cert.pem deleted file mode 100644 index aad1404ca..000000000 --- a/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/client-cert.pem +++ /dev/null @@ -1,19 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDBDCCAeygAwIBAgIBAzANBgkqhkiG9w0BAQsFADA8MTowOAYDVQQDDDFNeVNR -TF9TZXJ2ZXJfOC4wLjE5X0F1dG9fR2VuZXJhdGVkX0NBX0NlcnRpZmljYXRlMB4X -DTIwMDYxMTAzMzg0N1oXDTMwMDYwOTAzMzg0N1owQDE+MDwGA1UEAww1TXlTUUxf -U2VydmVyXzguMC4xOV9BdXRvX0dlbmVyYXRlZF9DbGllbnRfQ2VydGlmaWNhdGUw -ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDVYSWpOvCTupz82fc85Opv -EQ7rkB8X2oOMyBCpkyHKBIr1ZQgRDWBp9UVOASq3GnSElm6+T3Kb1QbOffa8GIlw -sjAueKdq5L2eSkmPIEQ7eoO5kEW+4V866hE1LeL/PmHg2lGP0iqZiJYtElhHNQO8 -3y9I7cm3xWMAA3SSWikVtpJRn3qIp2QSrH+tK+/HHbE5QwtPxdir4ULSCSOaM5Yh -Wi5Oto88TZqe1v7SXC864JVvO4LuS7TuSreCdWZyPXTJFBFeCEWSAxonKZrqHbBe -CwKML6/0NuzjaQ51c2tzmVI6xpHj3nnu4cSRx6Jf9WBm+35vm0wk4pohX3ptdzeV -AgMBAAGjDTALMAkGA1UdEwQCMAAwDQYJKoZIhvcNAQELBQADggEBAByQ5zSNeFUH -Aw7JlpZHtHaSEeiiyBHke20ziQ07BK1yi/ms2HAWwQkpZv149sjNuIRH8pkTmkZn -g8PDzSefjLbC9AsWpWV0XNV22T/cdobqLqMBDDZ2+5bsV+jTrOigWd9/AHVZ93PP -IJN8HJn6rtvo2l1bh/CdsX14uVSdofXnuWGabNTydqtMvmCerZsdf6qKqLL+PYwm -RDpgWiRUY7KPBSSlKm/9lJzA+bOe4dHeJzxWFVCJcbpoiTFs1je1V8kKQaHtuW39 -ifX6LTKUMlwEECCbDKM8Yq2tm8NjkjCcnFDtKg8zKGPUu+jrFMN5otiC3wnKcP7r -O9EkaPcgYH8= ------END CERTIFICATE----- diff --git a/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/client-key.pem b/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/client-key.pem deleted file mode 100644 index 6789d0291..000000000 --- a/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/client-key.pem +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEowIBAAKCAQEA1WElqTrwk7qc/Nn3POTqbxEO65AfF9qDjMgQqZMhygSK9WUI -EQ1gafVFTgEqtxp0hJZuvk9ym9UGzn32vBiJcLIwLninauS9nkpJjyBEO3qDuZBF -vuFfOuoRNS3i/z5h4NpRj9IqmYiWLRJYRzUDvN8vSO3Jt8VjAAN0klopFbaSUZ96 -iKdkEqx/rSvvxx2xOUMLT8XYq+FC0gkjmjOWIVouTraPPE2antb+0lwvOuCVbzuC -7ku07kq3gnVmcj10yRQRXghFkgMaJyma6h2wXgsCjC+v9Dbs42kOdXNrc5lSOsaR -49557uHEkceiX/VgZvt+b5tMJOKaIV96bXc3lQIDAQABAoIBAF7yjXmSOn7h6P0y -WCuGiTLG2mbDiLJqj2LTm2Z5i+2Cu/qZ7E76Ls63TxF4v3MemH5vGfQhEhR5ZD/6 -GRJ1sKKvB3WGRqjwA9gtojHH39S/nWGy6vYW/vMOOH37XyjIr3EIdIaUtFQBTSHd -Kd71niYrAbVn6fyWHolhADwnVmTMOl5OOAhCdEF4GN3b5aIhIu8BJ7EUzTtHBJIj -CAEfjZFjDs1y1cIgGFJkuIQxMfCpq5recU2qwip7YO6fk//WEjOPu7kSf5IEswL8 -jg1dea9rGBV6KaD2xsgsC6Ll6Sb4BbsrHMfflG3K2Lk3RdVqqTFp1Fn1PTLQE/1S -S/SZPYECgYEA9qYcHKHd0+Q5Ty5wgpxKGa4UCWkpwvfvyv4bh8qlmxueB+l2AIdo -ZvkM8gTPagPQ3WypAyC2b9iQu70uOJo1NizTtKnpjDdN1YpDjISJuS/P0x73gZwy -gmoM5AzMtN4D6IbxXtXnPaYICvwLKU80ouEN5ZPM4/ODLUu6gsp0v2UCgYEA3Xgi -zMC4JF0vEKEaK0H6QstaoXUmw/lToZGH3TEojBIkb/2LrHUclygtONh9kJSFb89/ -jbmRRLAOrx3HZKCNGUmF4H9k5OQyAIv6OGBinvLGqcbqnyNlI+Le8zxySYwKMlEj -EMrBCLmSyi0CGFrbZ3mlj/oCET/ql9rNvcK+DHECgYAEx5dH3sMjtgp+RFId1dWB -xePRgt4yTwewkVgLO5wV82UOljGZNQaK6Eyd7AXw8f38LHzh+KJQbIvxd2sL4cEi -OaAoohpKg0/Y0YMZl//rPMf0OWdmdZZs/I0fZjgZUSwWN3c59T8z7KG/RL8an9RP -S7kvN7wCttdV61/D5RR6GQKBgDxCe/WKWpBKaovzydMLWLTj7/0Oi0W3iXHkzzr4 -LTgvl4qBSofaNbVLUUKuZTv5rXUG2IYPf99YqCYtzBstNDc1MiAriaBeFtzfOW4t -i6gEFtoLLbuvPc3N5Sv5vn8Ug5G9UfU3td5R4AbyyCcoUZqOFuZd+EIJSiOXfXOs -kVmBAoGBAIU9aPAqhU5LX902oq8KsrpdySONqv5mtoStvl3wo95WIqXNEsFY60wO -q02jKQmJJ2MqhkJm2EoF2Mq8+40EZ5sz8LdgeQ/M0yQ9lAhPi4rftwhpe55Ma9dk -SE9X1c/DMCBEaIjJqVXdy0/EeArwpb8sHkguVVAZUWxzD+phm1gs ------END RSA PRIVATE KEY----- diff --git a/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/mongodb.pem b/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/mongodb.pem deleted file mode 100644 index 1fe94891a..000000000 --- a/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/mongodb.pem +++ /dev/null @@ -1,46 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDBDCCAeygAwIBAgIBAjANBgkqhkiG9w0BAQsFADA8MTowOAYDVQQDDDFNeVNR -TF9TZXJ2ZXJfOC4wLjE5X0F1dG9fR2VuZXJhdGVkX0NBX0NlcnRpZmljYXRlMB4X -DTIwMDYxMTAzMzg0NloXDTMwMDYwOTAzMzg0NlowQDE+MDwGA1UEAww1TXlTUUxf -U2VydmVyXzguMC4xOV9BdXRvX0dlbmVyYXRlZF9TZXJ2ZXJfQ2VydGlmaWNhdGUw -ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCcEnEm5hqP1EbEJycOz8Ua -NWp29QdpFUzTWhkKGhVXk+0msmNTw4NBAFB42moY44OU8wvDideOlJNhPRWveD8z -G2lxzJA91p0UK4et8ia9MmeuCGhdC9jxJ8X69WNlUiPyy0hI/ZsqRq9Z0C2eW0iL -JPXsy4X8Xpw3SFwoXf5pR9RFY5Pb2tuyxqmSestu2VXT/NQjJg4CVDR3mFcHPXZB -4elRzH0WshExEGkgy0bg20MJeRc2Qdb5Xx+EakbmwroDWaCn3NSGqQ7jv6Vw0doy -TGvS6h6RHBxnyqRfRgKGlCoOMG9/5+rFJC00QpCUG2vHXHWGoWlMlJ3foN7rj5v9 -AgMBAAGjDTALMAkGA1UdEwQCMAAwDQYJKoZIhvcNAQELBQADggEBAJ5zt2rj4Ag6 -zpN59AWC1Fur8g8l41ksHkSpKPp+PtyO/ngvbMqBpfmK1e7JCKZv/68QXfMyWWAI -hwalqZkXXWHKjuz3wE7dE25PXFXtGJtcZAaj10xt98fzdqt8lQSwh2kbfNwZIz1F -sgAStgE7+ZTcqTgvNB76Os1UK0to+/P0VBWktaVFdyub4Nc2SdPVnZNvrRBXBwOD -3V8ViwywDOFoE7DvCvwx/SVsvoC0Z4j3AMMovO6oHicP7uU83qsQgm1Qru3YeoLR -+DoVi7IPHbWvN7MqFYn3YjNlByO2geblY7MR0BlqbFlmFrqLsUfjsh2ys7/U/knC -dN/klu446fI= ------END CERTIFICATE----- ------BEGIN RSA PRIVATE KEY----- -MIIEowIBAAKCAQEAnBJxJuYaj9RGxCcnDs/FGjVqdvUHaRVM01oZChoVV5PtJrJj -U8ODQQBQeNpqGOODlPMLw4nXjpSTYT0Vr3g/MxtpccyQPdadFCuHrfImvTJnrgho -XQvY8SfF+vVjZVIj8stISP2bKkavWdAtnltIiyT17MuF/F6cN0hcKF3+aUfURWOT -29rbssapknrLbtlV0/zUIyYOAlQ0d5hXBz12QeHpUcx9FrIRMRBpIMtG4NtDCXkX -NkHW+V8fhGpG5sK6A1mgp9zUhqkO47+lcNHaMkxr0uoekRwcZ8qkX0YChpQqDjBv -f+fqxSQtNEKQlBtrx1x1hqFpTJSd36De64+b/QIDAQABAoIBAFiah66Dt9SruLkn -WR8piUaFyLlcBib8Nq9OWSTJBhDAJERxxb4KIvvGB+l0ZgNXNp5bFPSfzsZdRwZP -PX5uj8Kd71Dxx3mz211WESMJdEC42u+MSmN4lGLkJ5t/sDwXU91E1vbJM0ve8THV -4/Ag9qA4DX2vVZOeyqT/6YHpSsPNZplqzrbAiwrfHwkctHfgqwOf3QLfhmVQgfCS -VwidBldEUv2whSIiIxh4Rv5St4kA68IBCbJxdpOpyuQBkk6CkxZ7VN9FqOuSd4Pk -Wm7iWyBMZsCmELZh5XAXld4BEt87C5R4CvbPBDZxAv3THk1DNNvpy3PFQfwARRFb -SAToYMECgYEAyL7U8yxpzHDYWd3oCx6vTi9p9N/z0FfAkWrRF6dm4UcSklNiT1Aq -EOnTA+SaW8tV3E64gCWcY23gNP8so/ZseWj6L+peHwtchaP9+KB7yGw2A+05+lOx -VetLTjAOmfpiUXFe5w1q4C1RGhLjZjjzW+GvwdAuchQgUEFaomrV+PUCgYEAxwfH -cmVGFbAktcjU4HSRjKSfawCrut+3YUOLybyku3Q/hP9amG8qkVTFe95CTLjLe2D0 -ccaTTpofFEJ32COeck0g0Ujn/qQ+KXRoauOYs4FB1DtqMpqB78wufWEUpDpbd9/h -J+gJdC/IADd4tJW9zA92g8IA7ZtFmqDtiSpQ0ekCgYAQGkaorvJZpN+l7cf0RGTZ -h7IfI2vCVZer0n6tQA9fmLzjoe6r4AlPzAHSOR8sp9XeUy43kUzHKQQoHCPvjw/K -eWJAP7OHF/k2+x2fOPhU7mEy1W+mJdp+wt4Kio5RSaVjVQ3AyPG+w8PSrJszEvRq -dWMMz+851WV2KpfjmWBKlQKBgQC++4j4DZQV5aMkSKV1CIZOBf3vaIJhXKEUFQPD -PmB4fBEjpwCg+zNGp6iktt65zi17o8qMjrb1mtCt2SY04eD932LZUHNFlwcLMmes -Ad+aiDLJ24WJL1f16eDGcOyktlblDZB5gZ/ovJzXEGOkLXglosTfo77OQculmDy2 -/UL2WQKBgGeKasmGNfiYAcWio+KXgFkHXWtAXB9B91B1OFnCa40wx+qnl71MIWQH -PQ/CZFNWOfGiNEJIZjrHsfNJoeXkhq48oKcT0AVCDYyLV0VxDO4ejT95mGW6njNd -JpvmhwwAjOvuWVr0tn4iXlSK8irjlJHmwcRjLTJq97vE9fsA2MjI ------END RSA PRIVATE KEY----- diff --git a/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/private_key.pem b/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/private_key.pem deleted file mode 100644 index 8fbf6bdec..000000000 --- a/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/private_key.pem +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEpAIBAAKCAQEA1zVmMhPqpSPMmYkKh5wwlRD5XuS8YWJKEM6tjFx61VK8qxHE -YngkC2KnL5EuKAjQZIF3tJskwt0hAat047CCCZxrkNEpbVvSnvnk+A/8bg/Ww1n3 -qxzfifhsWfpUKlDnwrtH+ftt+5rZeEkf37XAPy7ZjzecAF9SDV6WSiPeAxUX2+hN -dId42Pf45woo4LFGUlQeagCFkD/R0dpNIMGwcnkKCUikiBqr2ijSIgvRtBfZ9fBG -jFGER2uE/Eay4AgcQsHue8skRwDCng8OnqtPnBtTytmqTy9V/BRgsVKUoksm6wsx -kUYwgHeaq7UCvlCm25SZ7yRyd4k8t0BKDf2h+wIDAQABAoIBAEQcrHmRACTADdNS -IjkFYALt2l8EOfMAbryfDSJtapr1kqz59JPNvmq0EIHnixo0n/APYdmReLML1ZR3 -tYkSpjVwgkLVUC1CcIjMQoGYXaZf8PLnGJHZk45RR8m6hsTV0mQ5bfBaeVa2jbma -OzJMjcnxg/3l9cPQZ2G/3AUfEPccMxOXp1KRz3mUQcGnKJGtDbN/kfmntcwYoxaE -Zg4RoeKAoMpK1SSHAiJKe7TnztINJ7uygR9XSzNd6auY8A3vomSIjpYO7XL+lh7L -izm4Ir3Gb/eCYBvWgQyQa2KCJgK/sQyEs3a09ngofSEUhQJQYhgZDwUj+fDDOGqj -hCZOA8ECgYEA+ZWuHdcUQ3ygYhLds2QcogUlIsx7C8n/Gk/FUrqqXJrTkuO0Eqqa -B47lCITvmn2zm0ODfSFIARgKEUEDLS/biZYv7SUTrFqBLcet+aGI7Dpv91CgB75R -tNzcIf8VxoiP0jPqdbh9mLbbxGi5Uc4p9TVXRljC4hkswaouebWee0sCgYEA3L2E -YB3kiHrhPI9LHS5Px9C1w+NOu5wP5snxrDGEgaFCvL6zgY6PflacppgnmTXl8D1x -im0IDKSw5dP3FFonSVXReq3CXDql7UnhfTCiLDahV7bLxTH42FofcBpDN3ERdOal -58RwQh6VrLkzQRVoObo+hbGlFiwwSAfQC509FhECgYBsRSBpVXo25IN2yBRg09cP -+gdoFyhxrsj5kw1YnB13WrrZh+oABv4WtUhp77E5ZbpaamlKCPwBbXpAjeFg4tfr -0bksuN7V79UGFQ9FsWuCfr8/nDwv38H2IbFlFhFONMOfPmJBey0Q6JJhm8R41mSh -OOiJXcv85UrjIH5U0hLUDQKBgQDVLOU5WcUJlPoOXSgiT0ZW5xWSzuOLRUUKEf6l -19BqzAzCcLy0orOrRAPW01xylt2v6/bJw1Ahva7k1ZZo/kOwjANYoZPxM+ZoSZBN -MXl8j2mzZuJVV1RFxItV3NcLJNPB/Lk+IbRz9kt/2f9InF7iWR3mSU/wIM6j0X+2 -p6yFsQKBgQCM/ldWb511lA+SNkqXB2P6WXAgAM/7+jwsNHX2ia2Ikufm4SUEKMSv -mti/nZkHDHsrHU4wb/2cOAywMELzv9EHzdcoenjBQP65OAc/1qWJs+LnBcCXfqKk -aHjEZW6+brkHdRGLLY3YAHlt/AUL+RsKPJfN72i/FSpmu+52G36eeQ== ------END RSA PRIVATE KEY----- diff --git a/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/public_key.pem b/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/public_key.pem deleted file mode 100644 index f9772b533..000000000 --- a/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/public_key.pem +++ /dev/null @@ -1,9 +0,0 @@ ------BEGIN PUBLIC KEY----- -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1zVmMhPqpSPMmYkKh5ww -lRD5XuS8YWJKEM6tjFx61VK8qxHEYngkC2KnL5EuKAjQZIF3tJskwt0hAat047CC -CZxrkNEpbVvSnvnk+A/8bg/Ww1n3qxzfifhsWfpUKlDnwrtH+ftt+5rZeEkf37XA -Py7ZjzecAF9SDV6WSiPeAxUX2+hNdId42Pf45woo4LFGUlQeagCFkD/R0dpNIMGw -cnkKCUikiBqr2ijSIgvRtBfZ9fBGjFGER2uE/Eay4AgcQsHue8skRwDCng8OnqtP -nBtTytmqTy9V/BRgsVKUoksm6wsxkUYwgHeaq7UCvlCm25SZ7yRyd4k8t0BKDf2h -+wIDAQAB ------END PUBLIC KEY----- diff --git a/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/server-cert.pem b/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/server-cert.pem deleted file mode 100644 index a2f9688df..000000000 --- a/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/server-cert.pem +++ /dev/null @@ -1,19 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDBDCCAeygAwIBAgIBAjANBgkqhkiG9w0BAQsFADA8MTowOAYDVQQDDDFNeVNR -TF9TZXJ2ZXJfOC4wLjE5X0F1dG9fR2VuZXJhdGVkX0NBX0NlcnRpZmljYXRlMB4X -DTIwMDYxMTAzMzg0NloXDTMwMDYwOTAzMzg0NlowQDE+MDwGA1UEAww1TXlTUUxf -U2VydmVyXzguMC4xOV9BdXRvX0dlbmVyYXRlZF9TZXJ2ZXJfQ2VydGlmaWNhdGUw -ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCcEnEm5hqP1EbEJycOz8Ua -NWp29QdpFUzTWhkKGhVXk+0msmNTw4NBAFB42moY44OU8wvDideOlJNhPRWveD8z -G2lxzJA91p0UK4et8ia9MmeuCGhdC9jxJ8X69WNlUiPyy0hI/ZsqRq9Z0C2eW0iL -JPXsy4X8Xpw3SFwoXf5pR9RFY5Pb2tuyxqmSestu2VXT/NQjJg4CVDR3mFcHPXZB -4elRzH0WshExEGkgy0bg20MJeRc2Qdb5Xx+EakbmwroDWaCn3NSGqQ7jv6Vw0doy -TGvS6h6RHBxnyqRfRgKGlCoOMG9/5+rFJC00QpCUG2vHXHWGoWlMlJ3foN7rj5v9 -AgMBAAGjDTALMAkGA1UdEwQCMAAwDQYJKoZIhvcNAQELBQADggEBAJ5zt2rj4Ag6 -zpN59AWC1Fur8g8l41ksHkSpKPp+PtyO/ngvbMqBpfmK1e7JCKZv/68QXfMyWWAI -hwalqZkXXWHKjuz3wE7dE25PXFXtGJtcZAaj10xt98fzdqt8lQSwh2kbfNwZIz1F -sgAStgE7+ZTcqTgvNB76Os1UK0to+/P0VBWktaVFdyub4Nc2SdPVnZNvrRBXBwOD -3V8ViwywDOFoE7DvCvwx/SVsvoC0Z4j3AMMovO6oHicP7uU83qsQgm1Qru3YeoLR -+DoVi7IPHbWvN7MqFYn3YjNlByO2geblY7MR0BlqbFlmFrqLsUfjsh2ys7/U/knC -dN/klu446fI= ------END CERTIFICATE----- diff --git a/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/server-key.pem b/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/server-key.pem deleted file mode 100644 index a1dfd5f78..000000000 --- a/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/server-key.pem +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEowIBAAKCAQEAnBJxJuYaj9RGxCcnDs/FGjVqdvUHaRVM01oZChoVV5PtJrJj -U8ODQQBQeNpqGOODlPMLw4nXjpSTYT0Vr3g/MxtpccyQPdadFCuHrfImvTJnrgho -XQvY8SfF+vVjZVIj8stISP2bKkavWdAtnltIiyT17MuF/F6cN0hcKF3+aUfURWOT -29rbssapknrLbtlV0/zUIyYOAlQ0d5hXBz12QeHpUcx9FrIRMRBpIMtG4NtDCXkX -NkHW+V8fhGpG5sK6A1mgp9zUhqkO47+lcNHaMkxr0uoekRwcZ8qkX0YChpQqDjBv -f+fqxSQtNEKQlBtrx1x1hqFpTJSd36De64+b/QIDAQABAoIBAFiah66Dt9SruLkn -WR8piUaFyLlcBib8Nq9OWSTJBhDAJERxxb4KIvvGB+l0ZgNXNp5bFPSfzsZdRwZP -PX5uj8Kd71Dxx3mz211WESMJdEC42u+MSmN4lGLkJ5t/sDwXU91E1vbJM0ve8THV -4/Ag9qA4DX2vVZOeyqT/6YHpSsPNZplqzrbAiwrfHwkctHfgqwOf3QLfhmVQgfCS -VwidBldEUv2whSIiIxh4Rv5St4kA68IBCbJxdpOpyuQBkk6CkxZ7VN9FqOuSd4Pk -Wm7iWyBMZsCmELZh5XAXld4BEt87C5R4CvbPBDZxAv3THk1DNNvpy3PFQfwARRFb -SAToYMECgYEAyL7U8yxpzHDYWd3oCx6vTi9p9N/z0FfAkWrRF6dm4UcSklNiT1Aq -EOnTA+SaW8tV3E64gCWcY23gNP8so/ZseWj6L+peHwtchaP9+KB7yGw2A+05+lOx -VetLTjAOmfpiUXFe5w1q4C1RGhLjZjjzW+GvwdAuchQgUEFaomrV+PUCgYEAxwfH -cmVGFbAktcjU4HSRjKSfawCrut+3YUOLybyku3Q/hP9amG8qkVTFe95CTLjLe2D0 -ccaTTpofFEJ32COeck0g0Ujn/qQ+KXRoauOYs4FB1DtqMpqB78wufWEUpDpbd9/h -J+gJdC/IADd4tJW9zA92g8IA7ZtFmqDtiSpQ0ekCgYAQGkaorvJZpN+l7cf0RGTZ -h7IfI2vCVZer0n6tQA9fmLzjoe6r4AlPzAHSOR8sp9XeUy43kUzHKQQoHCPvjw/K -eWJAP7OHF/k2+x2fOPhU7mEy1W+mJdp+wt4Kio5RSaVjVQ3AyPG+w8PSrJszEvRq -dWMMz+851WV2KpfjmWBKlQKBgQC++4j4DZQV5aMkSKV1CIZOBf3vaIJhXKEUFQPD -PmB4fBEjpwCg+zNGp6iktt65zi17o8qMjrb1mtCt2SY04eD932LZUHNFlwcLMmes -Ad+aiDLJ24WJL1f16eDGcOyktlblDZB5gZ/ovJzXEGOkLXglosTfo77OQculmDy2 -/UL2WQKBgGeKasmGNfiYAcWio+KXgFkHXWtAXB9B91B1OFnCa40wx+qnl71MIWQH -PQ/CZFNWOfGiNEJIZjrHsfNJoeXkhq48oKcT0AVCDYyLV0VxDO4ejT95mGW6njNd -JpvmhwwAjOvuWVr0tn4iXlSK8irjlJHmwcRjLTJq97vE9fsA2MjI ------END RSA PRIVATE KEY----- diff --git a/apps/emqx_auth_mysql/.gitignore b/apps/emqx_auth_mysql/.gitignore deleted file mode 100644 index bc6fa0f2f..000000000 --- a/apps/emqx_auth_mysql/.gitignore +++ /dev/null @@ -1,31 +0,0 @@ -.eunit -deps -*.so -.iml -.idea -*.o -*.beam -*.plt -erl_crash.dump -ebin -rel/example_project -.concrete/DEV_MODE -.rebar -.erlang.mk/ -emqx_auth_mysql.d -ct.coverdata -logs/ -test/ct.cover.spec -test/*.beam -cover/ -eunit.coverdata -data -.placeholder -_build/ -rebar.lock -erlang.mk -rebar3.crashdump -etc/emqx_auth_mysql.conf.rendered -.rebar3/ -*.swp -.DS_Store diff --git a/apps/emqx_auth_mysql/README.md b/apps/emqx_auth_mysql/README.md deleted file mode 100644 index e55a2103f..000000000 --- a/apps/emqx_auth_mysql/README.md +++ /dev/null @@ -1,167 +0,0 @@ -emqx_auth_mysql -=============== - -Authentication, ACL with MySQL Database. - -Notice: changed mysql driver to [mysql-otp](https://github.com/mysql-otp/mysql-otp). - -Features ---------- - -- Full *Authentication*, *Superuser*, *ACL* support -- IPv4, IPv6 and TLS support -- Connection pool by [ecpool](https://github.com/emqx/ecpool) -- Completely cover MySQL 5.7, MySQL 8 in our tests - -Build Plugin -------------- - -make && make tests - -Configure Plugin ----------------- - -File: etc/emqx_auth_mysql.conf - -``` -## MySQL server address. -## -## Value: Port | IP:Port -## -## Examples: 3306, 127.0.0.1:3306, localhost:3306 -auth.mysql.server = 127.0.0.1:3306 - -## MySQL pool size. -## -## Value: Number -auth.mysql.pool = 8 - -## MySQL username. -## -## Value: String -## auth.mysql.username = - -## MySQL Password. -## -## Value: String -## auth.mysql.password = - -## MySQL database. -## -## Value: String -auth.mysql.database = mqtt - -## Variables: %u = username, %c = clientid - -## Authentication query. -## -## Note that column names should be 'password' and 'salt' (if used). -## In case column names differ in your DB - please use aliases, -## e.g. "my_column_name as password". -## -## Value: SQL -## -## Variables: -## - %u: username -## - %c: clientid -## - %C: common name of client TLS cert -## - %d: subject of client TLS cert -## -auth.mysql.auth_query = select password from mqtt_user where username = '%u' limit 1 -## auth.mysql.auth_query = select password_hash as password from mqtt_user where username = '%u' limit 1 - -## Password hash. -## -## Value: plain | md5 | sha | sha256 | bcrypt -auth.mysql.password_hash = sha256 - -## sha256 with salt prefix -## auth.mysql.password_hash = salt,sha256 - -## bcrypt with salt only prefix -## auth.mysql.password_hash = salt,bcrypt - -## sha256 with salt suffix -## auth.mysql.password_hash = sha256,salt - -## pbkdf2 with macfun iterations dklen -## macfun: md4, md5, ripemd160, sha, sha224, sha256, sha384, sha512 -## auth.mysql.password_hash = pbkdf2,sha256,1000,20 - -## Superuser query. -## -## Value: SQL -## -## Variables: -## - %u: username -## - %c: clientid -## - %C: common name of client TLS cert -## - %d: subject of client TLS cert -auth.mysql.super_query = select is_superuser from mqtt_user where username = '%u' limit 1 - -## ACL query. -## -## Value: SQL -## -## Variables: -## - %a: ipaddr -## - %u: username -## - %c: clientid -## Note: You can add the 'ORDER BY' statement to control the rules match order -auth.mysql.acl_query = select allow, ipaddr, username, clientid, access, topic from mqtt_acl where ipaddr = '%a' or username = '%u' or username = '$all' or clientid = '%c' - -``` - -Import mqtt.sql ---------------- - -Import mqtt.sql into your database. - -Load Plugin ------------ - -./bin/emqx_ctl plugins load emqx_auth_mysql - -Auth Table ----------- - -Notice: This is a demo table. You could authenticate with any user table. - -```sql -CREATE TABLE `mqtt_user` ( - `id` int(11) unsigned NOT NULL AUTO_INCREMENT, - `username` varchar(100) DEFAULT NULL, - `password` varchar(100) DEFAULT NULL, - `salt` varchar(35) DEFAULT NULL, - `is_superuser` tinyint(1) DEFAULT 0, - `created` datetime DEFAULT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `mqtt_username` (`username`) -) ENGINE=MyISAM DEFAULT CHARSET=utf8; -``` - -ACL Table ----------- - -```sql -CREATE TABLE `mqtt_acl` ( - `id` int(11) unsigned NOT NULL AUTO_INCREMENT, - `allow` int(1) DEFAULT NULL COMMENT '0: deny, 1: allow', - `ipaddr` varchar(60) DEFAULT NULL COMMENT 'IpAddress', - `username` varchar(100) DEFAULT NULL COMMENT 'Username', - `clientid` varchar(100) DEFAULT NULL COMMENT 'ClientId', - `access` int(2) NOT NULL COMMENT '1: subscribe, 2: publish, 3: pubsub', - `topic` varchar(100) NOT NULL DEFAULT '' COMMENT 'Topic Filter', - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; -``` - -License -------- - -Apache License Version 2.0 - -Author ------- - -EMQ X Team. diff --git a/apps/emqx_auth_mysql/etc/emqx_auth_mysql.conf b/apps/emqx_auth_mysql/etc/emqx_auth_mysql.conf deleted file mode 100644 index 1c3d40059..000000000 --- a/apps/emqx_auth_mysql/etc/emqx_auth_mysql.conf +++ /dev/null @@ -1,131 +0,0 @@ -##-------------------------------------------------------------------- -## MySQL Auth/ACL Plugin -##-------------------------------------------------------------------- - -## MySQL server address. -## -## Value: Port | IP:Port -## -## Examples: 3306, 127.0.0.1:3306, localhost:3306 -auth.mysql.server = "127.0.0.1:3306" - -## MySQL pool size. -## -## Value: Number -auth.mysql.pool = 8 - -## MySQL username. -## -## Value: String -#auth.mysql.username = - -## MySQL password. -## -## Value: String -#auth.mysql.password = - -## MySQL database. -## -## Value: String -auth.mysql.database = mqtt - -## MySQL query timeout -## -## Value: Duration -## auth.mysql.query_timeout = 5s - -## Variables: %u = username, %c = clientid - -## Authentication query. -## -## Note that column names should be 'password' and 'salt' (if used). -## In case column names differ in your DB - please use aliases, -## e.g. "my_column_name as password". -## -## Value: SQL -## -## Variables: -## - %u: username -## - %c: clientid -## - %C: common name of client TLS cert -## - %d: subject of client TLS cert -## -auth.mysql.auth_query = "select password from mqtt_user where username = '%u' limit 1" -## auth.mysql.auth_query = select password_hash as password from mqtt_user where username = '%u' limit 1 - -## Password hash. -## -## Value: plain | md5 | sha | sha256 | bcrypt -auth.mysql.password_hash = sha256 - -## sha256 with salt prefix -## auth.mysql.password_hash = "salt,sha256" - -## bcrypt with salt only prefix -## auth.mysql.password_hash = "salt,bcrypt" - -## sha256 with salt suffix -## auth.mysql.password_hash = "sha256,salt" - -## pbkdf2 with macfun iterations dklen -## macfun: md4, md5, ripemd160, sha, sha224, sha256, sha384, sha512 -## auth.mysql.password_hash = "pbkdf2,sha256,1000,20" - -## Superuser query. -## -## Value: SQL -## -## Variables: -## - %u: username -## - %c: clientid -## - %C: common name of client TLS cert -## - %d: subject of client TLS cert -## -auth.mysql.super_query = "select is_superuser from mqtt_user where username = '%u' limit 1" - -## ACL query. -## -## Value: SQL -## -## Variables: -## - %a: ipaddr -## - %u: username -## - %c: clientid -## -## Note: You can add the 'ORDER BY' statement to control the rules match order -auth.mysql.acl_query = "select allow, ipaddr, username, clientid, access, topic from mqtt_acl where ipaddr = '%a' or username = '%u' or username = '$all' or clientid = '%c'" - -## Mysql ssl configuration. -## -## Value: on | off -## auth.mysql.ssl.enable = off - -## CA certificate. -## -## Value: File -#auth.mysql.ssl.cacertfile = /path/to/ca.pem - -## Client ssl certificate. -## -## Value: File -#auth.mysql.ssl.certfile = /path/to/your/clientcert.pem - -## Client ssl keyfile. -## -## Value: File -#auth.mysql.ssl.keyfile = /path/to/your/clientkey.pem - -## In mode verify_none the default behavior is to allow all x509-path -## validation errors. -## -## Value: true | false -#auth.mysql.ssl.verify = false - -## If not specified, the server's names returned in server's certificate is validated against -## what's provided `auth.mysql.server` config's host part. -## Setting to 'disable' will make EMQ X ignore unmatched server names. -## If set with a host name, the server's names returned in server's certificate is validated -## against this value. -## -## Value: String | disable -## auth.mysql.ssl.server_name_indication = disable diff --git a/apps/emqx_auth_mysql/include/emqx_auth_mysql.hrl b/apps/emqx_auth_mysql/include/emqx_auth_mysql.hrl deleted file mode 100644 index fca431e81..000000000 --- a/apps/emqx_auth_mysql/include/emqx_auth_mysql.hrl +++ /dev/null @@ -1,23 +0,0 @@ - --define(APP, emqx_auth_mysql). - --record(auth_metrics, { - success = 'client.auth.success', - failure = 'client.auth.failure', - ignore = 'client.auth.ignore' - }). - --record(acl_metrics, { - allow = 'client.acl.allow', - deny = 'client.acl.deny', - ignore = 'client.acl.ignore' - }). - --define(METRICS(Type), tl(tuple_to_list(#Type{}))). --define(METRICS(Type, K), #Type{}#Type.K). - --define(AUTH_METRICS, ?METRICS(auth_metrics)). --define(AUTH_METRICS(K), ?METRICS(auth_metrics, K)). - --define(ACL_METRICS, ?METRICS(acl_metrics)). --define(ACL_METRICS(K), ?METRICS(acl_metrics, K)). diff --git a/apps/emqx_auth_mysql/mqtt.sql b/apps/emqx_auth_mysql/mqtt.sql deleted file mode 100644 index 9635bee58..000000000 --- a/apps/emqx_auth_mysql/mqtt.sql +++ /dev/null @@ -1,41 +0,0 @@ - -DROP TABLE IF EXISTS `mqtt_acl`; - -CREATE TABLE `mqtt_acl` ( - `id` int(11) unsigned NOT NULL AUTO_INCREMENT, - `allow` int(1) DEFAULT NULL COMMENT '0: deny, 1: allow', - `ipaddr` varchar(60) DEFAULT NULL COMMENT 'IpAddress', - `username` varchar(100) DEFAULT NULL COMMENT 'Username', - `clientid` varchar(100) DEFAULT NULL COMMENT 'ClientId', - `access` int(2) NOT NULL COMMENT '1: subscribe, 2: publish, 3: pubsub', - `topic` varchar(100) NOT NULL DEFAULT '' COMMENT 'Topic Filter', - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - -LOCK TABLES `mqtt_acl` WRITE; - -INSERT INTO `mqtt_acl` (`id`, `allow`, `ipaddr`, `username`, `clientid`, `access`, `topic`) -VALUES - (1,1,NULL,'$all',NULL,2,'#'), - (2,0,NULL,'$all',NULL,1,'$SYS/#'), - (3,0,NULL,'$all',NULL,1,'eq #'), - (4,1,'127.0.0.1',NULL,NULL,2,'$SYS/#'), - (5,1,'127.0.0.1',NULL,NULL,2,'#'), - (6,1,NULL,'dashboard',NULL,1,'$SYS/#'); - -UNLOCK TABLES; - - -DROP TABLE IF EXISTS `mqtt_user`; - -CREATE TABLE `mqtt_user` ( - `id` int(11) unsigned NOT NULL AUTO_INCREMENT, - `username` varchar(100) DEFAULT NULL, - `password` varchar(100) DEFAULT NULL, - `salt` varchar(35) DEFAULT NULL, - `is_superuser` tinyint(1) DEFAULT 0, - `created` datetime DEFAULT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `mqtt_username` (`username`) -) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; - diff --git a/apps/emqx_auth_mysql/priv/emqx_auth_mysql.schema b/apps/emqx_auth_mysql/priv/emqx_auth_mysql.schema deleted file mode 100644 index fba25f41f..000000000 --- a/apps/emqx_auth_mysql/priv/emqx_auth_mysql.schema +++ /dev/null @@ -1,156 +0,0 @@ -%%-*- mode: erlang -*- -%% emqx_auth_mysql config mapping -{mapping, "auth.mysql.server", "emqx_auth_mysql.server", [ - {default, {"127.0.0.1", 3306}}, - {datatype, [integer, ip, string]} -]}. - -{mapping, "auth.mysql.pool", "emqx_auth_mysql.server", [ - {default, 8}, - {datatype, integer} -]}. - -{mapping, "auth.mysql.username", "emqx_auth_mysql.server", [ - {default, ""}, - {datatype, string} -]}. - -{mapping, "auth.mysql.password", "emqx_auth_mysql.server", [ - {default, ""}, - {datatype, string} -]}. - -{mapping, "auth.mysql.database", "emqx_auth_mysql.server", [ - {default, "mqtt"}, - {datatype, string} -]}. - -{mapping, "auth.mysql.query_timeout", "emqx_auth_mysql.server", [ - {default, ""}, - {datatype, string} -]}. - -{mapping, "auth.mysql.ssl.enable", "emqx_auth_mysql.server", [ - {default, off}, - {datatype, flag} -]}. - -{mapping, "auth.mysql.ssl.cafile", "emqx_auth_mysql.server", [ - {datatype, string} -]}. - -{mapping, "auth.mysql.ssl.cacertfile", "emqx_auth_mysql.server", [ - {datatype, string} -]}. - -%% FIXME: compatible with 4.0-4.2 version format, plan to delete in 5.0 -{mapping, "auth.mysql.ssl.certfile", "emqx_auth_mysql.server", [ - {datatype, string} -]}. - -{mapping, "auth.mysql.ssl.keyfile", "emqx_auth_mysql.server", [ - {datatype, string} -]}. - -{mapping, "auth.mysql.ssl.verify", "emqx_auth_mysql.server", [ - {default, false}, - {datatype, {enum, [true, false]}} -]}. - -{mapping, "auth.mysql.ssl.server_name_indication", "emqx_auth_mysql.server", [ - {datatype, string} -]}. - -{translation, "emqx_auth_mysql.server", fun(Conf) -> - {MyHost, MyPort} = - case cuttlefish:conf_get("auth.mysql.server", Conf) of - {Ip, Port} -> {Ip, Port}; - S -> case string:tokens(S, ":") of - [Domain] -> {Domain, 3306}; - [Domain, Port] -> {Domain, list_to_integer(Port)} - end - end, - Pool = cuttlefish:conf_get("auth.mysql.pool", Conf), - Username = cuttlefish:conf_get("auth.mysql.username", Conf), - Passwd = cuttlefish:conf_get("auth.mysql.password", Conf), - DB = cuttlefish:conf_get("auth.mysql.database", Conf), - Timeout = case cuttlefish:conf_get("auth.mysql.query_timeout", Conf) of - "" -> 300000; - Duration -> - case cuttlefish_duration:parse(Duration, ms) of - {error, Reason} -> error(Reason); - Ms when is_integer(Ms) -> Ms - end - end, - Options = [{pool_size, Pool}, - {auto_reconnect, 1}, - {host, MyHost}, - {port, MyPort}, - {user, Username}, - {password, Passwd}, - {database, DB}, - {encoding, utf8}, - {query_timeout, Timeout}, - {keep_alive, true}], - Filter = fun(Opts) -> [{K, V} || {K, V} <- Opts, V =/= undefined] end, - Options1 = - case cuttlefish:conf_get("auth.mysql.ssl.enable", Conf) of - true -> - %% FIXME: compatible with 4.0-4.2 version format, plan to delete in 5.0 - CA = cuttlefish:conf_get( - "auth.mysql.ssl.cacertfile", Conf, - cuttlefish:conf_get("auth.mysql.ssl.cafile", Conf, undefined) - ), - Cert = cuttlefish:conf_get("auth.mysql.ssl.certfile", Conf, undefined), - Key = cuttlefish:conf_get("auth.mysql.ssl.keyfile", Conf, undefined), - Verify = case cuttlefish:conf_get("auth.mysql.ssl.verify", Conf, false) of - true -> verify_peer; - false -> verify_none - end, - SNI = case cuttlefish:conf_get("auth.mysql.ssl.server_name_indication", Conf, undefined) of - "disable" -> disable; - SNI0 -> SNI0 - end, - Options ++ [{ssl, Filter([{server_name_indication, SNI}, - {cacertfile, CA}, - {certfile, Cert}, - {keyfile, Key}, - {verify, Verify} - ]) - }]; - _ -> - Options - end, - case inet:parse_address(MyHost) of - {ok, IpAddr} when tuple_size(IpAddr) =:= 8 -> - [{tcp_options, [inet6]} | Options1]; - _ -> - Options1 - end -end}. - -{mapping, "auth.mysql.auth_query", "emqx_auth_mysql.auth_query", [ - {datatype, string} -]}. - -{mapping, "auth.mysql.password_hash", "emqx_auth_mysql.password_hash", [ - {datatype, string} -]}. - -{mapping, "auth.mysql.super_query", "emqx_auth_mysql.super_query", [ - {datatype, string} -]}. - -{mapping, "auth.mysql.acl_query", "emqx_auth_mysql.acl_query", [ - {datatype, string} -]}. - -{translation, "emqx_auth_mysql.password_hash", fun(Conf) -> - HashValue = cuttlefish:conf_get("auth.mysql.password_hash", Conf), - case string:tokens(HashValue, ",") of - [Hash] -> list_to_atom(Hash); - [Prefix, Suffix] -> {list_to_atom(Prefix), list_to_atom(Suffix)}; - [Hash, MacFun, Iterations, Dklen] -> {list_to_atom(Hash), list_to_atom(MacFun), list_to_integer(Iterations), list_to_integer(Dklen)}; - _ -> plain - end -end}. diff --git a/apps/emqx_auth_mysql/rebar.config b/apps/emqx_auth_mysql/rebar.config deleted file mode 100644 index b86500e8f..000000000 --- a/apps/emqx_auth_mysql/rebar.config +++ /dev/null @@ -1,24 +0,0 @@ -{deps, - [ - {mysql, {git, "https://github.com/emqx/mysql-otp", {tag, "1.7.1"}}} - ]}. - -{edoc_opts, [{preprocess, true}]}. -{erl_opts, [warn_unused_vars, - warn_shadow_vars, - warn_unused_import, - warn_obsolete_guard, - debug_info, - compressed, - {parse_transform} - ]}. -{overrides, [{add, [{erl_opts, [compressed]}]}]}. - -{xref_checks, [undefined_function_calls, undefined_functions, - locals_not_used, deprecated_function_calls, - warnings_as_errors, deprecated_functions - ]}. - -{cover_enabled, true}. -{cover_opts, [verbose]}. -{cover_export_enabled, true}. diff --git a/apps/emqx_auth_mysql/src/emqx_acl_mysql.erl b/apps/emqx_auth_mysql/src/emqx_acl_mysql.erl deleted file mode 100644 index ef4acea94..000000000 --- a/apps/emqx_auth_mysql/src/emqx_acl_mysql.erl +++ /dev/null @@ -1,119 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_acl_mysql). - --include("emqx_auth_mysql.hrl"). - --include_lib("emqx/include/emqx.hrl"). --include_lib("emqx/include/logger.hrl"). - -%% ACL Callbacks --export([ register_metrics/0 - , check_acl/5 - , description/0 - ]). - --spec(register_metrics() -> ok). -register_metrics() -> - lists:foreach(fun emqx_metrics:ensure/1, ?ACL_METRICS). - -check_acl(ClientInfo, PubSub, Topic, NoMatchAction, #{pool := Pool} = State) -> - case do_check_acl(Pool, ClientInfo, PubSub, Topic, NoMatchAction, State) of - ok -> emqx_metrics:inc(?ACL_METRICS(ignore)), ok; - {stop, allow} -> emqx_metrics:inc(?ACL_METRICS(allow)), {stop, allow}; - {stop, deny} -> emqx_metrics:inc(?ACL_METRICS(deny)), {stop, deny} - end. - -do_check_acl(_Pool, #{username := <<$$, _/binary>>}, _PubSub, _Topic, _NoMatchAction, _State) -> - ok; -do_check_acl(Pool, ClientInfo, PubSub, Topic, _NoMatchAction, #{acl_query := {AclSql, AclParams}}) -> - case emqx_auth_mysql_cli:query(Pool, AclSql, AclParams, ClientInfo) of - {ok, _Columns, []} -> ok; - {ok, _Columns, Rows} -> - Rules = filter(PubSub, compile(Rows)), - case match(ClientInfo, Topic, Rules) of - {matched, allow} -> {stop, allow}; - {matched, deny} -> {stop, deny}; - nomatch -> ok - end; - {error, Reason} -> - ?LOG(error, "[MySQL] do_check_acl error: ~p~n", [Reason]), - ok - end. - -match(_ClientInfo, _Topic, []) -> - nomatch; - -match(ClientInfo, Topic, [Rule|Rules]) -> - case emqx_access_rule:match(ClientInfo, Topic, Rule) of - nomatch -> - match(ClientInfo, Topic, Rules); - {matched, AllowDeny} -> - {matched, AllowDeny} - end. - -filter(PubSub, Rules) -> - [Term || Term = {_, _, Access, _} <- Rules, - Access =:= PubSub orelse Access =:= pubsub]. - -compile(Rows) -> - compile(Rows, []). -compile([], Acc) -> - Acc; -compile([[Allow, IpAddr, Username, ClientId, Access, Topic]|T], Acc) -> - Who = who(IpAddr, Username, ClientId), - Term = {allow(Allow), Who, access(Access), [topic(Topic)]}, - compile(T, [emqx_access_rule:compile(Term) | Acc]). - -who(_, <<"$all">>, _) -> - all; -who(null, null, null) -> - throw(undefined_who); -who(CIDR, Username, ClientId) -> - Cols = [{ipaddr, b2l(CIDR)}, {user, Username}, {client, ClientId}], - case [{C, V} || {C, V} <- Cols, not empty(V)] of - [Who] -> Who; - Conds -> {'and', Conds} - end. - -allow(1) -> allow; -allow(0) -> deny; -allow(<<"1">>) -> allow; -allow(<<"0">>) -> deny. - -access(1) -> subscribe; -access(2) -> publish; -access(3) -> pubsub; -access(<<"1">>) -> subscribe; -access(<<"2">>) -> publish; -access(<<"3">>) -> pubsub. - -topic(<<"eq ", Topic/binary>>) -> - {eq, Topic}; -topic(Topic) -> - Topic. - -description() -> - "ACL with Mysql". - -b2l(null) -> null; -b2l(B) -> binary_to_list(B). - -empty(null) -> true; -empty("") -> true; -empty(<<>>) -> true; -empty(_) -> false. diff --git a/apps/emqx_auth_mysql/src/emqx_auth_mysql.app.src b/apps/emqx_auth_mysql/src/emqx_auth_mysql.app.src deleted file mode 100644 index 8a0d116cc..000000000 --- a/apps/emqx_auth_mysql/src/emqx_auth_mysql.app.src +++ /dev/null @@ -1,14 +0,0 @@ -{application, emqx_auth_mysql, - [{description, "EMQ X Authentication/ACL with MySQL"}, - {vsn, "4.4.0"}, % strict semver, bump manually! - {modules, []}, - {registered, [emqx_auth_mysql_sup]}, - {applications, [kernel,stdlib,mysql,ecpool]}, - {mod, {emqx_auth_mysql_app,[]}}, - {env, []}, - {licenses, ["Apache-2.0"]}, - {maintainers, ["EMQ X Team "]}, - {links, [{"Homepage", "https://emqx.io/"}, - {"Github", "https://github.com/emqx/emqx-auth-mysql"} - ]} - ]}. diff --git a/apps/emqx_auth_mysql/src/emqx_auth_mysql.erl b/apps/emqx_auth_mysql/src/emqx_auth_mysql.erl deleted file mode 100644 index eba7ef081..000000000 --- a/apps/emqx_auth_mysql/src/emqx_auth_mysql.erl +++ /dev/null @@ -1,91 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_auth_mysql). - --include("emqx_auth_mysql.hrl"). - --include_lib("emqx/include/emqx.hrl"). --include_lib("emqx/include/logger.hrl"). --include_lib("emqx/include/types.hrl"). - --export([ register_metrics/0 - , check/3 - , description/0 - ]). - --define(EMPTY(Username), (Username =:= undefined orelse Username =:= <<>>)). - --spec(register_metrics() -> ok). -register_metrics() -> - lists:foreach(fun emqx_metrics:ensure/1, ?AUTH_METRICS). - -check(ClientInfo = #{password := Password}, AuthResult, - #{auth_query := {AuthSql, AuthParams}, - super_query := SuperQuery, - hash_type := HashType, - pool := Pool}) -> - CheckPass = case emqx_auth_mysql_cli:query(Pool, AuthSql, AuthParams, ClientInfo) of - {ok, [<<"password">>], [[PassHash]]} -> - check_pass({PassHash, Password}, HashType); - {ok, [<<"password">>, <<"salt">>], [[PassHash, Salt]]} -> - check_pass({PassHash, Salt, Password}, HashType); - {ok, _Columns, []} -> - {error, not_found}; - {error, Reason} -> - ?LOG(error, "[MySQL] query '~p' failed: ~p", [AuthSql, Reason]), - {error, Reason} - end, - case CheckPass of - ok -> - emqx_metrics:inc(?AUTH_METRICS(success)), - {stop, AuthResult#{is_superuser => is_superuser(Pool, SuperQuery, ClientInfo), - anonymous => false, - auth_result => success}}; - {error, not_found} -> - emqx_metrics:inc(?AUTH_METRICS(ignore)), ok; - {error, ResultCode} -> - ?LOG(error, "[MySQL] Auth from mysql failed: ~p", [ResultCode]), - emqx_metrics:inc(?AUTH_METRICS(failure)), - {stop, AuthResult#{auth_result => ResultCode, anonymous => false}} - end. - -%%-------------------------------------------------------------------- -%% Is Superuser? -%%-------------------------------------------------------------------- - --spec(is_superuser(atom(), maybe({string(), list()}), emqx_types:client()) -> boolean()). -is_superuser(_Pool, undefined, _ClientInfo) -> false; -is_superuser(Pool, {SuperSql, Params}, ClientInfo) -> - case emqx_auth_mysql_cli:query(Pool, SuperSql, Params, ClientInfo) of - {ok, [_Super], [[1]]} -> - true; - {ok, [_Super], [[_False]]} -> - false; - {ok, [_Super], []} -> - false; - {error, _Error} -> - false - end. - -check_pass(Password, HashType) -> - case emqx_passwd:check_pass(Password, HashType) of - ok -> ok; - {error, _Reason} -> {error, not_authorized} - end. - -description() -> "Authentication with MySQL". - diff --git a/apps/emqx_auth_mysql/src/emqx_auth_mysql_app.erl b/apps/emqx_auth_mysql/src/emqx_auth_mysql_app.erl deleted file mode 100644 index ec1e25e6b..000000000 --- a/apps/emqx_auth_mysql/src/emqx_auth_mysql_app.erl +++ /dev/null @@ -1,74 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_auth_mysql_app). - --behaviour(application). - --emqx_plugin(auth). - --include("emqx_auth_mysql.hrl"). - --import(emqx_auth_mysql_cli, [parse_query/1]). - -%% Application callbacks --export([ start/2 - , prep_stop/1 - , stop/1 - ]). - -%%-------------------------------------------------------------------- -%% Application callbacks -%%-------------------------------------------------------------------- - -start(_StartType, _StartArgs) -> - {ok, Sup} = emqx_auth_mysql_sup:start_link(), - _ = if_enabled(auth_query, fun load_auth_hook/1), - _ = if_enabled(acl_query, fun load_acl_hook/1), - - {ok, Sup}. - -prep_stop(State) -> - emqx:unhook('client.authenticate', {emqx_auth_mysql, check}), - emqx:unhook('client.check_acl', {emqx_acl_mysql, check_acl}), - State. - -stop(_State) -> - ok. - -load_auth_hook(AuthQuery) -> - ok = emqx_auth_mysql:register_metrics(), - SuperQuery = parse_query(application:get_env(?APP, super_query, undefined)), - {ok, HashType} = application:get_env(?APP, password_hash), - Params = #{auth_query => AuthQuery, - super_query => SuperQuery, - hash_type => HashType, - pool => ?APP}, - emqx:hook('client.authenticate', {emqx_auth_mysql, check, [Params]}). - -load_acl_hook(AclQuery) -> - ok = emqx_acl_mysql:register_metrics(), - emqx:hook('client.check_acl', {emqx_acl_mysql, check_acl, [#{acl_query => AclQuery, pool =>?APP}]}). - -%%-------------------------------------------------------------------- -%% Internal function -%%-------------------------------------------------------------------- - -if_enabled(Cfg, Fun) -> - case application:get_env(?APP, Cfg) of - {ok, Query} -> Fun(parse_query(Query)); - undefined -> ok - end. diff --git a/apps/emqx_auth_mysql/src/emqx_auth_mysql_cli.erl b/apps/emqx_auth_mysql/src/emqx_auth_mysql_cli.erl deleted file mode 100644 index cf3be3426..000000000 --- a/apps/emqx_auth_mysql/src/emqx_auth_mysql_cli.erl +++ /dev/null @@ -1,91 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_auth_mysql_cli). - --behaviour(ecpool_worker). - --include("emqx_auth_mysql.hrl"). --include_lib("emqx/include/emqx.hrl"). --include_lib("emqx/include/logger.hrl"). - --export([ parse_query/1 - , connect/1 - , query/4 - ]). - -%%-------------------------------------------------------------------- -%% Avoid SQL Injection: Parse SQL to Parameter Query. -%%-------------------------------------------------------------------- - -parse_query(undefined) -> - undefined; -parse_query(Sql) -> - case re:run(Sql, "'%[ucCad]'", [global, {capture, all, list}]) of - {match, Variables} -> - Params = [Var || [Var] <- Variables], - {re:replace(Sql, "'%[ucCad]'", "?", [global, {return, list}]), Params}; - nomatch -> - {Sql, []} - end. - -%%-------------------------------------------------------------------- -%% MySQL Connect/Query -%%-------------------------------------------------------------------- - -connect(Options) -> - case mysql:start_link(Options) of - {ok, Pid} -> {ok, Pid}; - ignore -> {error, ignore}; - {error, Reason = {{_, {error, econnrefused}}, _}} -> - ?LOG(error, "[MySQL] Can't connect to MySQL server: Connection refused."), - {error, Reason}; - {error, Reason = {ErrorCode, _, Error}} -> - ?LOG(error, "[MySQL] Can't connect to MySQL server: ~p - ~p", [ErrorCode, Error]), - {error, Reason}; - {error, Reason} -> - ?LOG(error, "[MySQL] Can't connect to MySQL server: ~p", [Reason]), - {error, Reason} - end. - -query(Pool, Sql, Params, ClientInfo) -> - ecpool:with_client(Pool, fun(C) -> mysql:query(C, Sql, replvar(Params, ClientInfo)) end). - -replvar(Params, ClientInfo) -> - replvar(Params, ClientInfo, []). - -replvar([], _ClientInfo, Acc) -> - lists:reverse(Acc); - -replvar(["'%u'" | Params], ClientInfo, Acc) -> - replvar(Params, ClientInfo, [safe_get(username, ClientInfo) | Acc]); -replvar(["'%c'" | Params], ClientInfo = #{clientid := ClientId}, Acc) -> - replvar(Params, ClientInfo, [ClientId | Acc]); -replvar(["'%a'" | Params], ClientInfo = #{peerhost := IpAddr}, Acc) -> - replvar(Params, ClientInfo, [inet_parse:ntoa(IpAddr) | Acc]); -replvar(["'%C'" | Params], ClientInfo, Acc) -> - replvar(Params, ClientInfo, [safe_get(cn, ClientInfo)| Acc]); -replvar(["'%d'" | Params], ClientInfo, Acc) -> - replvar(Params, ClientInfo, [safe_get(dn, ClientInfo)| Acc]); -replvar([Param | Params], ClientInfo, Acc) -> - replvar(Params, ClientInfo, [Param | Acc]). - -safe_get(K, ClientInfo) -> - bin(maps:get(K, ClientInfo, "undefined")). - -bin(A) when is_atom(A) -> atom_to_binary(A, utf8); -bin(B) when is_binary(B) -> B; -bin(X) -> X. diff --git a/apps/emqx_auth_mysql/src/emqx_auth_mysql_sup.erl b/apps/emqx_auth_mysql/src/emqx_auth_mysql_sup.erl deleted file mode 100644 index 70f4987a3..000000000 --- a/apps/emqx_auth_mysql/src/emqx_auth_mysql_sup.erl +++ /dev/null @@ -1,40 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_auth_mysql_sup). - --behaviour(supervisor). - --include("emqx_auth_mysql.hrl"). - --export([start_link/0]). - -%% Supervisor callbacks --export([init/1]). - -start_link() -> - supervisor:start_link({local, ?MODULE}, ?MODULE, []). - -%%-------------------------------------------------------------------- -%% Supervisor callbacks -%%-------------------------------------------------------------------- - -init([]) -> - %% MySQL Connection Pool. - {ok, Server} = application:get_env(?APP, server), - PoolSpec = ecpool:pool_spec(?APP, ?APP, emqx_auth_mysql_cli, Server), - {ok, {{one_for_one, 10, 100}, [PoolSpec]}}. - diff --git a/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE.erl b/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE.erl deleted file mode 100644 index 0ae81435d..000000000 --- a/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE.erl +++ /dev/null @@ -1,235 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_auth_mysql_SUITE). - --compile(export_all). --compile(nowarn_export_all). - --define(APP, emqx_auth_mysql). - --include_lib("emqx/include/emqx.hrl"). --include_lib("eunit/include/eunit.hrl"). --include_lib("common_test/include/ct.hrl"). - --define(DROP_ACL_TABLE, <<"DROP TABLE IF EXISTS mqtt_acl">>). - --define(CREATE_ACL_TABLE, <<"CREATE TABLE mqtt_acl (" - " id int(11) unsigned NOT NULL AUTO_INCREMENT," - " allow int(1) DEFAULT NULL COMMENT '0: deny, 1: allow'," - " ipaddr varchar(60) DEFAULT NULL COMMENT 'IpAddress'," - " username varchar(100) DEFAULT NULL COMMENT 'Username'," - " clientid varchar(100) DEFAULT NULL COMMENT 'ClientId'," - " access int(2) NOT NULL COMMENT '1: subscribe, 2: publish, 3: pubsub'," - " topic varchar(100) NOT NULL DEFAULT '' COMMENT 'Topic Filter'," - " PRIMARY KEY (`id`)" - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4">>). - --define(INIT_ACL, <<"INSERT INTO mqtt_acl (id, allow, ipaddr, username, clientid, access, topic)" - "VALUES (1,1,'127.0.0.1','u1','c1',1,'t1')," - "(2,0,'127.0.0.1','u2','c2',1,'t1')," - "(3,1,'10.10.0.110','u1','c1',1,'t1')," - "(4,1,'127.0.0.1','u3','c3',3,'t1')">>). - --define(DROP_AUTH_TABLE, <<"DROP TABLE IF EXISTS `mqtt_user`">>). - --define(CREATE_AUTH_TABLE, <<"CREATE TABLE `mqtt_user` (" - "`id` int(11) unsigned NOT NULL AUTO_INCREMENT," - "`username` varchar(100) DEFAULT NULL," - "`password` varchar(100) DEFAULT NULL," - "`salt` varchar(100) DEFAULT NULL," - "`is_superuser` tinyint(1) DEFAULT 0," - "`created` datetime DEFAULT NULL," - "PRIMARY KEY (`id`)," - "UNIQUE KEY `mqtt_username` (`username`)" - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4">>). - --define(INIT_AUTH, <<"INSERT INTO mqtt_user (id, is_superuser, username, password, salt)" - "VALUES (1, 1, 'plain', 'plain', 'salt')," - "(2, 0, 'md5', '1bc29b36f623ba82aaf6724fd3b16718', 'salt')," - "(3, 0, 'sha', 'd8f4590320e1343a915b6394170650a8f35d6926', 'salt')," - "(4, 0, 'sha256', '5d5b09f6dcb2d53a5fffc60c4ac0d55fabdf556069d6631545f42aa6e3500f2e', 'salt')," - "(5, 0, 'pbkdf2_password', 'cdedb5281bb2f801565a1122b2563515', 'ATHENA.MIT.EDUraeburn')," - "(6, 0, 'bcrypt_foo', '$2a$12$sSS8Eg.ovVzaHzi1nUHYK.HbUIOdlQI0iS22Q5rd5z.JVVYH6sfm6', '$2a$12$sSS8Eg.ovVzaHzi1nUHYK.')," - "(7, 0, 'bcrypt', '$2y$16$rEVsDarhgHYB0TGnDFJzyu5f.T.Ha9iXMTk9J36NCMWWM7O16qyaK', 'salt')," - "(8, 0, 'bcrypt_wrong', '$2y$16$rEVsDarhgHYB0TGnDFJzyu', 'salt')">>). - -%%-------------------------------------------------------------------- -%% Setups -%%-------------------------------------------------------------------- - -all() -> - emqx_ct:all(?MODULE). - -init_per_suite(Cfg) -> - emqx_ct_helpers:start_apps([emqx_auth_mysql], fun set_special_configs/1), - init_mysql_data(), - Cfg. - -end_per_suite(_) -> - deinit_mysql_data(), - emqx_ct_helpers:stop_apps([emqx_auth_mysql]), - ok. - -set_special_configs(emqx) -> - application:set_env(emqx, allow_anonymous, false), - application:set_env(emqx, acl_nomatch, deny), - application:set_env(emqx, enable_acl_cache, false), - application:set_env(emqx, plugins_loaded_file, - emqx_ct_helpers:deps_path(emqx, "test/emqx_SUITE_data/loaded_plugins")); - -set_special_configs(_App) -> - ok. - -init_mysql_data() -> - {ok, Pid} = ecpool_worker:client(gproc_pool:pick_worker({ecpool, ?APP})), - %% Users - ok = mysql:query(Pid, ?DROP_AUTH_TABLE), - ok = mysql:query(Pid, ?CREATE_AUTH_TABLE), - ok = mysql:query(Pid, ?INIT_AUTH), - - %% ACLs - ok = mysql:query(Pid, ?DROP_ACL_TABLE), - ok = mysql:query(Pid, ?CREATE_ACL_TABLE), - ok = mysql:query(Pid, ?INIT_ACL). - -deinit_mysql_data() -> - {ok, Pid} = ecpool_worker:client(gproc_pool:pick_worker({ecpool, ?APP})), - ok = mysql:query(Pid, ?DROP_AUTH_TABLE), - ok = mysql:query(Pid, ?DROP_ACL_TABLE). - -%%-------------------------------------------------------------------- -%% Test cases -%%-------------------------------------------------------------------- - -t_check_acl(_) -> - User0 = #{zone => external,peerhost => {127,0,0,1}}, - deny = emqx_access_control:check_acl(User0, subscribe, <<"t1">>), - User1 = #{zone => external, clientid => <<"c1">>, username => <<"u1">>, peerhost => {127,0,0,1}}, - User2 = #{zone => external, clientid => <<"c2">>, username => <<"u2">>, peerhost => {127,0,0,1}}, - allow = emqx_access_control:check_acl(User1, subscribe, <<"t1">>), - deny = emqx_access_control:check_acl(User2, subscribe, <<"t1">>), - - User3 = #{zone => external, peerhost => {10,10,0,110}, clientid => <<"c1">>, username => <<"u1">>}, - User4 = #{zone => external, peerhost => {10,10,10,110}, clientid => <<"c1">>, username => <<"u1">>}, - allow = emqx_access_control:check_acl(User3, subscribe, <<"t1">>), - allow = emqx_access_control:check_acl(User3, subscribe, <<"t1">>), - deny = emqx_access_control:check_acl(User3, subscribe, <<"t2">>),%% nomatch -> ignore -> emqx acl - deny = emqx_access_control:check_acl(User4, subscribe, <<"t1">>),%% nomatch -> ignore -> emqx acl - User5 = #{zone => external, peerhost => {127,0,0,1}, clientid => <<"c3">>, username => <<"u3">>}, - allow = emqx_access_control:check_acl(User5, subscribe, <<"t1">>), - allow = emqx_access_control:check_acl(User5, publish, <<"t1">>). - -t_acl_super(_Config) -> - reload([{password_hash, plain}, - {auth_query, "select password from mqtt_user where username = '%u' limit 1"}]), - {ok, C} = emqtt:start_link([{host, "localhost"}, - {clientid, <<"simpleClient">>}, - {username, <<"plain">>}, - {password, <<"plain">>}]), - {ok, _} = emqtt:connect(C), - timer:sleep(10), - emqtt:subscribe(C, <<"TopicA">>, qos2), - timer:sleep(1000), - emqtt:publish(C, <<"TopicA">>, <<"Payload">>, qos2), - timer:sleep(1000), - receive - {publish, #{payload := Payload}} -> - ?assertEqual(<<"Payload">>, Payload) - after - 1000 -> - ct:fail({receive_timeout, <<"Payload">>}), - ok - end, - emqtt:disconnect(C). - -t_check_auth(_) -> - Plain = #{clientid => <<"client1">>, username => <<"plain">>, zone => external}, - Md5 = #{clientid => <<"md5">>, username => <<"md5">>, zone => external}, - Sha = #{clientid => <<"sha">>, username => <<"sha">>, zone => external}, - Sha256 = #{clientid => <<"sha256">>, username => <<"sha256">>, zone => external}, - Pbkdf2 = #{clientid => <<"pbkdf2_password">>, username => <<"pbkdf2_password">>, zone => external}, - BcryptFoo = #{clientid => <<"bcrypt_foo">>, username => <<"bcrypt_foo">>, zone => external}, - User1 = #{clientid => <<"bcrypt_foo">>, username => <<"user">>, zone => external}, - Bcrypt = #{clientid => <<"bcrypt">>, username => <<"bcrypt">>, zone => external}, - BcryptWrong = #{clientid => <<"bcrypt_wrong">>, username => <<"bcrypt_wrong">>, zone => external}, - reload([{password_hash, plain}]), - {ok,#{is_superuser := true}} = - emqx_access_control:authenticate(Plain#{password => <<"plain">>}), - reload([{password_hash, md5}]), - {ok,#{is_superuser := false}} = - emqx_access_control:authenticate(Md5#{password => <<"md5">>}), - reload([{password_hash, sha}]), - {ok,#{is_superuser := false}} = - emqx_access_control:authenticate(Sha#{password => <<"sha">>}), - reload([{password_hash, sha256}]), - {ok,#{is_superuser := false}} = - emqx_access_control:authenticate(Sha256#{password => <<"sha256">>}), - reload([{password_hash, bcrypt}]), - {ok,#{is_superuser := false}} = - emqx_access_control:authenticate(Bcrypt#{password => <<"password">>}), - {error, not_authorized} = - emqx_access_control:authenticate(BcryptWrong#{password => <<"password">>}), - %%pbkdf2 sha - reload([{password_hash, {pbkdf2, sha, 1, 16}}, - {auth_query, "select password, salt from mqtt_user where username = '%u' limit 1"}]), - {ok,#{is_superuser := false}} = - emqx_access_control:authenticate(Pbkdf2#{password => <<"password">>}), - reload([{password_hash, {salt, bcrypt}}]), - {ok,#{is_superuser := false}} = - emqx_access_control:authenticate(BcryptFoo#{password => <<"foo">>}), - {error, _} = emqx_access_control:authenticate(User1#{password => <<"foo">>}), - {error, not_authorized} = emqx_access_control:authenticate(Bcrypt#{password => <<"password">>}). - -t_comment_config(_) -> - application:stop(?APP), - [application:unset_env(?APP, Par) || Par <- [acl_query, auth_query]], - application:start(?APP). - -t_placeholders(_) -> - ClientA = #{username => <<"plain">>, clientid => <<"plain">>, zone => external}, - - reload([{password_hash, plain}, - {auth_query, "select password from mqtt_user where username = '%u' and 'a_cn_val' = '%C' limit 1"}]), - {error, not_authorized} = - emqx_access_control:authenticate(ClientA#{password => <<"plain">>}), - {error, not_authorized} = - emqx_access_control:authenticate(ClientA#{password => <<"plain">>, cn => undefined}), - {ok, _} = - emqx_access_control:authenticate(ClientA#{password => <<"plain">>, cn => <<"a_cn_val">>}), - - reload([{auth_query, "select password from mqtt_user where username = '%c' and 'a_dn_val' = '%d' limit 1"}]), - {error, not_authorized} = - emqx_access_control:authenticate(ClientA#{password => <<"plain">>}), - {error, not_authorized} = - emqx_access_control:authenticate(ClientA#{password => <<"plain">>, dn => undefined}), - {ok, _} = - emqx_access_control:authenticate(ClientA#{password => <<"plain">>, dn => <<"a_dn_val">>}), - - reload([{auth_query, "select password from mqtt_user where username = '%u' and '192.168.1.5' = '%a' limit 1"}]), - {error, not_authorized} = - emqx_access_control:authenticate(ClientA#{password => <<"plain">>}), - {ok, _} = - emqx_access_control:authenticate(ClientA#{password => <<"plain">>, peerhost => {192,168,1,5}}). - -%%-------------------------------------------------------------------- -%% Internal funcs -%%-------------------------------------------------------------------- - -reload(Config) when is_list(Config) -> - application:stop(?APP), - [application:set_env(?APP, K, V) || {K, V} <- Config], - application:start(?APP). diff --git a/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/ca-key.pem b/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/ca-key.pem deleted file mode 100644 index e9717011e..000000000 --- a/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/ca-key.pem +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEpAIBAAKCAQEA0kGUBi9NDp65jgdxKfizIfuSr2wpwb44yM9SuP4oUQSULOA2 -4iFpLR/c5FAYHU81y9Vx91dQjdZfffaBZuv2zVvteXUkol8Nez7boKbo2E41MTew -8edtNKZAQVvnaHAC2NCZxjchCzUCDEoUUcl+cIERZ8R48FBqK5iTVcMRIx1akwus -+dhBqP0ykA5TGOWZkJrLM9aUXSPQha9+wXlOpkvu0Ur2nkX8PPJnifWao9UShSar -ll1IqPZNCSlZMwcFYcQNBCpdvITUUYlHvMRQV64bUpOxUGDuJkQL3dLKBlNuBRlJ -BcjBAKw7rFnwwHZcMmQ9tan/dZzpzwjo/T0XjwIDAQABAoIBAQCSHvUqnzDkWjcG -l/Fzg92qXlYBCCC0/ugj1sHcwvVt6Mq5rVE3MpUPwTcYjPlVVTlD4aEEjm/zQuq2 -ddxUlOS+r4aIhHrjRT/vSS4FpjnoKeIZxGR6maVxk6DQS3i1QjMYT1CvSpzyVvKH -a+xXMrtmoKxh+085ZAmFJtIuJhUA2yEa4zggCxWnvz8ecLClUPfVDPhdLBHc3KmL -CRpHEC6L/wanvDPRdkkzfKyaJuIJlTDaCg63AY5sDkTW2I57iI/nJ3haSeidfQKz -39EfbnM1A/YprIakafjAu3frBIsjBVcxwGihZmL/YriTHjOggJF841kT5zFkkv2L -/530Wk6xAoGBAOqZLZ4DIi/zLndEOz1mRbUfjc7GQUdYplBnBwJ22VdS0P4TOXnd -UbJth2MA92NM7ocTYVFl4TVIZY/Y+Prxk7KQdHWzR7JPpKfx9OEVgtSqV0vF9eGI -rKp79Y1T4Mvc3UcQCXX6TP7nHLihEzpS8odm2LW4txrOiLsn4Fq/IWrLAoGBAOVv -6U4tm3lImotUupKLZPKEBYwruo9qRysoug9FiorP4TjaBVOfltiiHbAQD6aGfVtN -SZpZZtrs17wL7Xl4db5asgMcZd+8Hkfo5siR7AuGW9FZloOjDcXb5wCh9EvjJ74J -Cjw7RqyVymq9t7IP6wnVwj5Ck48YhlOZCz/mzlnNAoGAWq7NYFgLvgc9feLFF23S -IjpJQZWHJEITP98jaYNxbfzYRm49+GphqxwFinKULjFNvq7yHlnIXSVYBOu1CqOZ -GRwXuGuNmlKI7lZr9xmukfAqgGLMMdr4C4qRF4lFyufcLRz42z7exmWlx4ST/yaT -E13hBRWayeTuG5JFei6Jh1MCgYEAqmX4LyC+JFBgvvQZcLboLRkSCa18bADxhENG -FAuAvmFvksqRRC71WETmqZj0Fqgxt7pp3KFjO1rFSprNLvbg85PmO1s+6fCLyLpX -lESTu2d5D71qhK93jigooxalGitFm+SY3mzjq0/AOpBWOn+J/w7rqVPGxXLgaHv0 -l+vx+00CgYBOvo9/ImjwYii2jFl+sHEoCzlvpITi2temRlT2j6ulSjCLJgjwEFw9 -8e+vvfQumQOsutakUVyURrkMGNDiNlIv8kv5YLCCkrwN22E6Ghyi69MJUvHQXkc/ -QZhjn/luyfpB5f/BeHFS2bkkxAXo+cfG45ApY3Qfz6/7o+H+vDa6/A== ------END RSA PRIVATE KEY----- diff --git a/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/ca.pem b/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/ca.pem deleted file mode 100644 index 00b31d8a4..000000000 --- a/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/ca.pem +++ /dev/null @@ -1,19 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDAzCCAeugAwIBAgIBATANBgkqhkiG9w0BAQsFADA8MTowOAYDVQQDDDFNeVNR -TF9TZXJ2ZXJfOC4wLjE5X0F1dG9fR2VuZXJhdGVkX0NBX0NlcnRpZmljYXRlMB4X -DTIwMDYxMTAzMzg0NloXDTMwMDYwOTAzMzg0NlowPDE6MDgGA1UEAwwxTXlTUUxf -U2VydmVyXzguMC4xOV9BdXRvX0dlbmVyYXRlZF9DQV9DZXJ0aWZpY2F0ZTCCASIw -DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANJBlAYvTQ6euY4HcSn4syH7kq9s -KcG+OMjPUrj+KFEElCzgNuIhaS0f3ORQGB1PNcvVcfdXUI3WX332gWbr9s1b7Xl1 -JKJfDXs+26Cm6NhONTE3sPHnbTSmQEFb52hwAtjQmcY3IQs1AgxKFFHJfnCBEWfE -ePBQaiuYk1XDESMdWpMLrPnYQaj9MpAOUxjlmZCayzPWlF0j0IWvfsF5TqZL7tFK -9p5F/DzyZ4n1mqPVEoUmq5ZdSKj2TQkpWTMHBWHEDQQqXbyE1FGJR7zEUFeuG1KT -sVBg7iZEC93SygZTbgUZSQXIwQCsO6xZ8MB2XDJkPbWp/3Wc6c8I6P09F48CAwEA -AaMQMA4wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEADKz6bIpP5anp -GgLB0jkclRWuMlS4qqIt4itSsMXPJ/ezpHwECixmgW2TIQl6S1woRkUeMxhT2/Ay -Sn/7aKxuzRagyE5NEGOvrOuAP5RO2ZdNJ/X3/Rh533fK1sOTEEbSsWUvW6iSkZef -rsfZBVP32xBhRWkKRdLeLB4W99ADMa0IrTmZPCXHSSE2V4e1o6zWLXcOZeH1Qh8N -SkelBweR+8r1Fbvy1r3s7eH7DCbYoGEDVLQGOLvzHKBisQHmoDnnF5E9g1eeNRdg -o+vhOKfYCOzeNREJIqS42PHcGhdNRk90ycigPmfUJclz1mDHoMjKR2S5oosTpr65 -tNPx3CL7GA== ------END CERTIFICATE----- diff --git a/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/client-cert.pem b/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/client-cert.pem deleted file mode 100644 index aad1404ca..000000000 --- a/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/client-cert.pem +++ /dev/null @@ -1,19 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDBDCCAeygAwIBAgIBAzANBgkqhkiG9w0BAQsFADA8MTowOAYDVQQDDDFNeVNR -TF9TZXJ2ZXJfOC4wLjE5X0F1dG9fR2VuZXJhdGVkX0NBX0NlcnRpZmljYXRlMB4X -DTIwMDYxMTAzMzg0N1oXDTMwMDYwOTAzMzg0N1owQDE+MDwGA1UEAww1TXlTUUxf -U2VydmVyXzguMC4xOV9BdXRvX0dlbmVyYXRlZF9DbGllbnRfQ2VydGlmaWNhdGUw -ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDVYSWpOvCTupz82fc85Opv -EQ7rkB8X2oOMyBCpkyHKBIr1ZQgRDWBp9UVOASq3GnSElm6+T3Kb1QbOffa8GIlw -sjAueKdq5L2eSkmPIEQ7eoO5kEW+4V866hE1LeL/PmHg2lGP0iqZiJYtElhHNQO8 -3y9I7cm3xWMAA3SSWikVtpJRn3qIp2QSrH+tK+/HHbE5QwtPxdir4ULSCSOaM5Yh -Wi5Oto88TZqe1v7SXC864JVvO4LuS7TuSreCdWZyPXTJFBFeCEWSAxonKZrqHbBe -CwKML6/0NuzjaQ51c2tzmVI6xpHj3nnu4cSRx6Jf9WBm+35vm0wk4pohX3ptdzeV -AgMBAAGjDTALMAkGA1UdEwQCMAAwDQYJKoZIhvcNAQELBQADggEBAByQ5zSNeFUH -Aw7JlpZHtHaSEeiiyBHke20ziQ07BK1yi/ms2HAWwQkpZv149sjNuIRH8pkTmkZn -g8PDzSefjLbC9AsWpWV0XNV22T/cdobqLqMBDDZ2+5bsV+jTrOigWd9/AHVZ93PP -IJN8HJn6rtvo2l1bh/CdsX14uVSdofXnuWGabNTydqtMvmCerZsdf6qKqLL+PYwm -RDpgWiRUY7KPBSSlKm/9lJzA+bOe4dHeJzxWFVCJcbpoiTFs1je1V8kKQaHtuW39 -ifX6LTKUMlwEECCbDKM8Yq2tm8NjkjCcnFDtKg8zKGPUu+jrFMN5otiC3wnKcP7r -O9EkaPcgYH8= ------END CERTIFICATE----- diff --git a/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/client-key.pem b/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/client-key.pem deleted file mode 100644 index 6789d0291..000000000 --- a/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/client-key.pem +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEowIBAAKCAQEA1WElqTrwk7qc/Nn3POTqbxEO65AfF9qDjMgQqZMhygSK9WUI -EQ1gafVFTgEqtxp0hJZuvk9ym9UGzn32vBiJcLIwLninauS9nkpJjyBEO3qDuZBF -vuFfOuoRNS3i/z5h4NpRj9IqmYiWLRJYRzUDvN8vSO3Jt8VjAAN0klopFbaSUZ96 -iKdkEqx/rSvvxx2xOUMLT8XYq+FC0gkjmjOWIVouTraPPE2antb+0lwvOuCVbzuC -7ku07kq3gnVmcj10yRQRXghFkgMaJyma6h2wXgsCjC+v9Dbs42kOdXNrc5lSOsaR -49557uHEkceiX/VgZvt+b5tMJOKaIV96bXc3lQIDAQABAoIBAF7yjXmSOn7h6P0y -WCuGiTLG2mbDiLJqj2LTm2Z5i+2Cu/qZ7E76Ls63TxF4v3MemH5vGfQhEhR5ZD/6 -GRJ1sKKvB3WGRqjwA9gtojHH39S/nWGy6vYW/vMOOH37XyjIr3EIdIaUtFQBTSHd -Kd71niYrAbVn6fyWHolhADwnVmTMOl5OOAhCdEF4GN3b5aIhIu8BJ7EUzTtHBJIj -CAEfjZFjDs1y1cIgGFJkuIQxMfCpq5recU2qwip7YO6fk//WEjOPu7kSf5IEswL8 -jg1dea9rGBV6KaD2xsgsC6Ll6Sb4BbsrHMfflG3K2Lk3RdVqqTFp1Fn1PTLQE/1S -S/SZPYECgYEA9qYcHKHd0+Q5Ty5wgpxKGa4UCWkpwvfvyv4bh8qlmxueB+l2AIdo -ZvkM8gTPagPQ3WypAyC2b9iQu70uOJo1NizTtKnpjDdN1YpDjISJuS/P0x73gZwy -gmoM5AzMtN4D6IbxXtXnPaYICvwLKU80ouEN5ZPM4/ODLUu6gsp0v2UCgYEA3Xgi -zMC4JF0vEKEaK0H6QstaoXUmw/lToZGH3TEojBIkb/2LrHUclygtONh9kJSFb89/ -jbmRRLAOrx3HZKCNGUmF4H9k5OQyAIv6OGBinvLGqcbqnyNlI+Le8zxySYwKMlEj -EMrBCLmSyi0CGFrbZ3mlj/oCET/ql9rNvcK+DHECgYAEx5dH3sMjtgp+RFId1dWB -xePRgt4yTwewkVgLO5wV82UOljGZNQaK6Eyd7AXw8f38LHzh+KJQbIvxd2sL4cEi -OaAoohpKg0/Y0YMZl//rPMf0OWdmdZZs/I0fZjgZUSwWN3c59T8z7KG/RL8an9RP -S7kvN7wCttdV61/D5RR6GQKBgDxCe/WKWpBKaovzydMLWLTj7/0Oi0W3iXHkzzr4 -LTgvl4qBSofaNbVLUUKuZTv5rXUG2IYPf99YqCYtzBstNDc1MiAriaBeFtzfOW4t -i6gEFtoLLbuvPc3N5Sv5vn8Ug5G9UfU3td5R4AbyyCcoUZqOFuZd+EIJSiOXfXOs -kVmBAoGBAIU9aPAqhU5LX902oq8KsrpdySONqv5mtoStvl3wo95WIqXNEsFY60wO -q02jKQmJJ2MqhkJm2EoF2Mq8+40EZ5sz8LdgeQ/M0yQ9lAhPi4rftwhpe55Ma9dk -SE9X1c/DMCBEaIjJqVXdy0/EeArwpb8sHkguVVAZUWxzD+phm1gs ------END RSA PRIVATE KEY----- diff --git a/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/private_key.pem b/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/private_key.pem deleted file mode 100644 index 8fbf6bdec..000000000 --- a/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/private_key.pem +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEpAIBAAKCAQEA1zVmMhPqpSPMmYkKh5wwlRD5XuS8YWJKEM6tjFx61VK8qxHE -YngkC2KnL5EuKAjQZIF3tJskwt0hAat047CCCZxrkNEpbVvSnvnk+A/8bg/Ww1n3 -qxzfifhsWfpUKlDnwrtH+ftt+5rZeEkf37XAPy7ZjzecAF9SDV6WSiPeAxUX2+hN -dId42Pf45woo4LFGUlQeagCFkD/R0dpNIMGwcnkKCUikiBqr2ijSIgvRtBfZ9fBG -jFGER2uE/Eay4AgcQsHue8skRwDCng8OnqtPnBtTytmqTy9V/BRgsVKUoksm6wsx -kUYwgHeaq7UCvlCm25SZ7yRyd4k8t0BKDf2h+wIDAQABAoIBAEQcrHmRACTADdNS -IjkFYALt2l8EOfMAbryfDSJtapr1kqz59JPNvmq0EIHnixo0n/APYdmReLML1ZR3 -tYkSpjVwgkLVUC1CcIjMQoGYXaZf8PLnGJHZk45RR8m6hsTV0mQ5bfBaeVa2jbma -OzJMjcnxg/3l9cPQZ2G/3AUfEPccMxOXp1KRz3mUQcGnKJGtDbN/kfmntcwYoxaE -Zg4RoeKAoMpK1SSHAiJKe7TnztINJ7uygR9XSzNd6auY8A3vomSIjpYO7XL+lh7L -izm4Ir3Gb/eCYBvWgQyQa2KCJgK/sQyEs3a09ngofSEUhQJQYhgZDwUj+fDDOGqj -hCZOA8ECgYEA+ZWuHdcUQ3ygYhLds2QcogUlIsx7C8n/Gk/FUrqqXJrTkuO0Eqqa -B47lCITvmn2zm0ODfSFIARgKEUEDLS/biZYv7SUTrFqBLcet+aGI7Dpv91CgB75R -tNzcIf8VxoiP0jPqdbh9mLbbxGi5Uc4p9TVXRljC4hkswaouebWee0sCgYEA3L2E -YB3kiHrhPI9LHS5Px9C1w+NOu5wP5snxrDGEgaFCvL6zgY6PflacppgnmTXl8D1x -im0IDKSw5dP3FFonSVXReq3CXDql7UnhfTCiLDahV7bLxTH42FofcBpDN3ERdOal -58RwQh6VrLkzQRVoObo+hbGlFiwwSAfQC509FhECgYBsRSBpVXo25IN2yBRg09cP -+gdoFyhxrsj5kw1YnB13WrrZh+oABv4WtUhp77E5ZbpaamlKCPwBbXpAjeFg4tfr -0bksuN7V79UGFQ9FsWuCfr8/nDwv38H2IbFlFhFONMOfPmJBey0Q6JJhm8R41mSh -OOiJXcv85UrjIH5U0hLUDQKBgQDVLOU5WcUJlPoOXSgiT0ZW5xWSzuOLRUUKEf6l -19BqzAzCcLy0orOrRAPW01xylt2v6/bJw1Ahva7k1ZZo/kOwjANYoZPxM+ZoSZBN -MXl8j2mzZuJVV1RFxItV3NcLJNPB/Lk+IbRz9kt/2f9InF7iWR3mSU/wIM6j0X+2 -p6yFsQKBgQCM/ldWb511lA+SNkqXB2P6WXAgAM/7+jwsNHX2ia2Ikufm4SUEKMSv -mti/nZkHDHsrHU4wb/2cOAywMELzv9EHzdcoenjBQP65OAc/1qWJs+LnBcCXfqKk -aHjEZW6+brkHdRGLLY3YAHlt/AUL+RsKPJfN72i/FSpmu+52G36eeQ== ------END RSA PRIVATE KEY----- diff --git a/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/public_key.pem b/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/public_key.pem deleted file mode 100644 index f9772b533..000000000 --- a/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/public_key.pem +++ /dev/null @@ -1,9 +0,0 @@ ------BEGIN PUBLIC KEY----- -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1zVmMhPqpSPMmYkKh5ww -lRD5XuS8YWJKEM6tjFx61VK8qxHEYngkC2KnL5EuKAjQZIF3tJskwt0hAat047CC -CZxrkNEpbVvSnvnk+A/8bg/Ww1n3qxzfifhsWfpUKlDnwrtH+ftt+5rZeEkf37XA -Py7ZjzecAF9SDV6WSiPeAxUX2+hNdId42Pf45woo4LFGUlQeagCFkD/R0dpNIMGw -cnkKCUikiBqr2ijSIgvRtBfZ9fBGjFGER2uE/Eay4AgcQsHue8skRwDCng8OnqtP -nBtTytmqTy9V/BRgsVKUoksm6wsxkUYwgHeaq7UCvlCm25SZ7yRyd4k8t0BKDf2h -+wIDAQAB ------END PUBLIC KEY----- diff --git a/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/server-cert.pem b/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/server-cert.pem deleted file mode 100644 index a2f9688df..000000000 --- a/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/server-cert.pem +++ /dev/null @@ -1,19 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDBDCCAeygAwIBAgIBAjANBgkqhkiG9w0BAQsFADA8MTowOAYDVQQDDDFNeVNR -TF9TZXJ2ZXJfOC4wLjE5X0F1dG9fR2VuZXJhdGVkX0NBX0NlcnRpZmljYXRlMB4X -DTIwMDYxMTAzMzg0NloXDTMwMDYwOTAzMzg0NlowQDE+MDwGA1UEAww1TXlTUUxf -U2VydmVyXzguMC4xOV9BdXRvX0dlbmVyYXRlZF9TZXJ2ZXJfQ2VydGlmaWNhdGUw -ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCcEnEm5hqP1EbEJycOz8Ua -NWp29QdpFUzTWhkKGhVXk+0msmNTw4NBAFB42moY44OU8wvDideOlJNhPRWveD8z -G2lxzJA91p0UK4et8ia9MmeuCGhdC9jxJ8X69WNlUiPyy0hI/ZsqRq9Z0C2eW0iL -JPXsy4X8Xpw3SFwoXf5pR9RFY5Pb2tuyxqmSestu2VXT/NQjJg4CVDR3mFcHPXZB -4elRzH0WshExEGkgy0bg20MJeRc2Qdb5Xx+EakbmwroDWaCn3NSGqQ7jv6Vw0doy -TGvS6h6RHBxnyqRfRgKGlCoOMG9/5+rFJC00QpCUG2vHXHWGoWlMlJ3foN7rj5v9 -AgMBAAGjDTALMAkGA1UdEwQCMAAwDQYJKoZIhvcNAQELBQADggEBAJ5zt2rj4Ag6 -zpN59AWC1Fur8g8l41ksHkSpKPp+PtyO/ngvbMqBpfmK1e7JCKZv/68QXfMyWWAI -hwalqZkXXWHKjuz3wE7dE25PXFXtGJtcZAaj10xt98fzdqt8lQSwh2kbfNwZIz1F -sgAStgE7+ZTcqTgvNB76Os1UK0to+/P0VBWktaVFdyub4Nc2SdPVnZNvrRBXBwOD -3V8ViwywDOFoE7DvCvwx/SVsvoC0Z4j3AMMovO6oHicP7uU83qsQgm1Qru3YeoLR -+DoVi7IPHbWvN7MqFYn3YjNlByO2geblY7MR0BlqbFlmFrqLsUfjsh2ys7/U/knC -dN/klu446fI= ------END CERTIFICATE----- diff --git a/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/server-key.pem b/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/server-key.pem deleted file mode 100644 index a1dfd5f78..000000000 --- a/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/server-key.pem +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEowIBAAKCAQEAnBJxJuYaj9RGxCcnDs/FGjVqdvUHaRVM01oZChoVV5PtJrJj -U8ODQQBQeNpqGOODlPMLw4nXjpSTYT0Vr3g/MxtpccyQPdadFCuHrfImvTJnrgho -XQvY8SfF+vVjZVIj8stISP2bKkavWdAtnltIiyT17MuF/F6cN0hcKF3+aUfURWOT -29rbssapknrLbtlV0/zUIyYOAlQ0d5hXBz12QeHpUcx9FrIRMRBpIMtG4NtDCXkX -NkHW+V8fhGpG5sK6A1mgp9zUhqkO47+lcNHaMkxr0uoekRwcZ8qkX0YChpQqDjBv -f+fqxSQtNEKQlBtrx1x1hqFpTJSd36De64+b/QIDAQABAoIBAFiah66Dt9SruLkn -WR8piUaFyLlcBib8Nq9OWSTJBhDAJERxxb4KIvvGB+l0ZgNXNp5bFPSfzsZdRwZP -PX5uj8Kd71Dxx3mz211WESMJdEC42u+MSmN4lGLkJ5t/sDwXU91E1vbJM0ve8THV -4/Ag9qA4DX2vVZOeyqT/6YHpSsPNZplqzrbAiwrfHwkctHfgqwOf3QLfhmVQgfCS -VwidBldEUv2whSIiIxh4Rv5St4kA68IBCbJxdpOpyuQBkk6CkxZ7VN9FqOuSd4Pk -Wm7iWyBMZsCmELZh5XAXld4BEt87C5R4CvbPBDZxAv3THk1DNNvpy3PFQfwARRFb -SAToYMECgYEAyL7U8yxpzHDYWd3oCx6vTi9p9N/z0FfAkWrRF6dm4UcSklNiT1Aq -EOnTA+SaW8tV3E64gCWcY23gNP8so/ZseWj6L+peHwtchaP9+KB7yGw2A+05+lOx -VetLTjAOmfpiUXFe5w1q4C1RGhLjZjjzW+GvwdAuchQgUEFaomrV+PUCgYEAxwfH -cmVGFbAktcjU4HSRjKSfawCrut+3YUOLybyku3Q/hP9amG8qkVTFe95CTLjLe2D0 -ccaTTpofFEJ32COeck0g0Ujn/qQ+KXRoauOYs4FB1DtqMpqB78wufWEUpDpbd9/h -J+gJdC/IADd4tJW9zA92g8IA7ZtFmqDtiSpQ0ekCgYAQGkaorvJZpN+l7cf0RGTZ -h7IfI2vCVZer0n6tQA9fmLzjoe6r4AlPzAHSOR8sp9XeUy43kUzHKQQoHCPvjw/K -eWJAP7OHF/k2+x2fOPhU7mEy1W+mJdp+wt4Kio5RSaVjVQ3AyPG+w8PSrJszEvRq -dWMMz+851WV2KpfjmWBKlQKBgQC++4j4DZQV5aMkSKV1CIZOBf3vaIJhXKEUFQPD -PmB4fBEjpwCg+zNGp6iktt65zi17o8qMjrb1mtCt2SY04eD932LZUHNFlwcLMmes -Ad+aiDLJ24WJL1f16eDGcOyktlblDZB5gZ/ovJzXEGOkLXglosTfo77OQculmDy2 -/UL2WQKBgGeKasmGNfiYAcWio+KXgFkHXWtAXB9B91B1OFnCa40wx+qnl71MIWQH -PQ/CZFNWOfGiNEJIZjrHsfNJoeXkhq48oKcT0AVCDYyLV0VxDO4ejT95mGW6njNd -JpvmhwwAjOvuWVr0tn4iXlSK8irjlJHmwcRjLTJq97vE9fsA2MjI ------END RSA PRIVATE KEY----- diff --git a/apps/emqx_auth_pgsql/.gitignore b/apps/emqx_auth_pgsql/.gitignore deleted file mode 100644 index 672d34c0c..000000000 --- a/apps/emqx_auth_pgsql/.gitignore +++ /dev/null @@ -1,20 +0,0 @@ -ebin -.rebar -.eunit -.DS_Store -.erlang.mk/ -deps/ -emqx_auth_pgsql.d -ct.coverdata -logs/ -test/ct.cover.spec -test/*.beam -data/ -.DS_Store -cover/ -eunit.coverdata -_build/ -rebar.lock -erlang.mk -*.conf.rendered -.rebar3/ diff --git a/apps/emqx_auth_pgsql/README.md b/apps/emqx_auth_pgsql/README.md deleted file mode 100644 index a8f5d723f..000000000 --- a/apps/emqx_auth_pgsql/README.md +++ /dev/null @@ -1,183 +0,0 @@ -emqx_auth_pgsql -=============== - -Authentication/ACL with PostgreSQL Database. - -Build Plugin ------------- - -make && make tests - -Configuration -------------- - -File: etc/emqx_auth_pgsql.conf - -``` -## PostgreSQL server address. -## -## Value: Port | IP:Port -## -## Examples: 5432, 127.0.0.1:5432, localhost:5432 -auth.pgsql.server = 127.0.0.1:5432 - -## PostgreSQL pool size. -## -## Value: Number -auth.pgsql.pool = 8 - -## PostgreSQL username. -## -## Value: String -auth.pgsql.username = root - -## PostgreSQL password. -## -## Value: String -## auth.pgsql.password = - -## PostgreSQL database. -## -## Value: String -auth.pgsql.database = mqtt - -## PostgreSQL database encoding. -## -## Value: String -auth.pgsql.encoding = utf8 - -## Whether to enable SSL connection. -## -## Value: true | false -auth.pgsql.ssl.enable = false - -## SSL keyfile. -## -## Value: File -## auth.pgsql.ssl_opts.keyfile = - -## SSL certfile. -## -## Value: File -## auth.pgsql.ssl_opts.certfile = - -## SSL cacertfile. -## -## Value: File -## auth.pgsql.ssl_opts.cacertfile = - -## Authentication query. -## -## Value: SQL -## -## Variables: -## - %u: username -## - %c: clientid -## -auth.pgsql.auth_query = select password from mqtt_user where username = '%u' limit 1 - -## Password hash. -## -## Value: plain | md5 | sha | sha256 | bcrypt -auth.pgsql.password_hash = sha256 - -## sha256 with salt prefix -## auth.pgsql.password_hash = salt,sha256 - -## sha256 with salt suffix -## auth.pgsql.password_hash = sha256,salt - -## bcrypt with salt prefix -## auth.pgsql.password_hash = salt,bcrypt - -## pbkdf2 with macfun iterations dklen -## macfun: md4, md5, ripemd160, sha, sha224, sha256, sha384, sha512 -## auth.pgsql.password_hash = pbkdf2,sha256,1000,20 - -## Superuser query. -## -## Value: SQL -## -## Variables: -## - %u: username -## - %c: clientid -auth.pgsql.super_query = select is_superuser from mqtt_user where username = '%u' limit 1 - -## ACL query. Comment this query, the ACL will be disabled. -## -## Value: SQL -## -## Variables: -## - %a: ipaddress -## - %u: username -## - %c: clientid -auth.pgsql.acl_query = select allow, ipaddr, username, clientid, access, topic from mqtt_acl where ipaddr = '%a' or username = '%u' or username = '$all' or clientid = '%c' -``` - -Load Plugin ------------ - -./bin/emqx_ctl plugins load emqx_auth_pgsql - -Auth Table ----------- - -Notice: This is a demo table. You could authenticate with any user table. - -```sql -CREATE TABLE mqtt_user ( - id SERIAL primary key, - is_superuser boolean, - username character varying(100), - password character varying(100), - salt character varying(40) -) -``` - -ACL Table ---------- - -```sql -CREATE TABLE mqtt_acl ( - id SERIAL primary key, - allow integer, - ipaddr character varying(60), - username character varying(100), - clientid character varying(100), - access integer, - topic character varying(100) -) - -INSERT INTO mqtt_acl (id, allow, ipaddr, username, clientid, access, topic) -VALUES - (1,1,NULL,'$all',NULL,2,'#'), - (2,0,NULL,'$all',NULL,1,'$SYS/#'), - (3,0,NULL,'$all',NULL,1,'eq #'), - (5,1,'127.0.0.1',NULL,NULL,2,'$SYS/#'), - (6,1,'127.0.0.1',NULL,NULL,2,'#'), - (7,1,NULL,'dashboard',NULL,1,'$SYS/#'); -``` -**allow:** Client's permission to access a topic. '0' means that the client does not have permission to access the topic, '1' means that the client have permission to access the topic. - -**ipaddr:** Client IP address. For all ip addresses it can be '$all' or 'NULL'. - -**username:** Client username. For all users it can be '$all' or 'NULL'. - -**clientid:** Client id. For all client ids it can be '$all' or 'NULL'. - -**access:** Operations that the client can perform. '1' means that the client can subscribe to a topic, '2' means that the client can publish to a topic, '3' means that the client can subscribe and can publish to a topic. - -**topic:** Topic name. Topic wildcards are supported. - -**Notice that only one value allowed for ipaddr, username and clientid fields.** - -License -------- - -Apache License Version 2.0 - -Author ------- - -EMQ X Team. - diff --git a/apps/emqx_auth_pgsql/etc/emqx_auth_pgsql.conf b/apps/emqx_auth_pgsql/etc/emqx_auth_pgsql.conf deleted file mode 100644 index 6f7018210..000000000 --- a/apps/emqx_auth_pgsql/etc/emqx_auth_pgsql.conf +++ /dev/null @@ -1,132 +0,0 @@ -##-------------------------------------------------------------------- -## PostgreSQL Auth/ACL Plugin -##-------------------------------------------------------------------- - -## PostgreSQL server address. -## -## Value: Port | IP:Port -## -## Examples: 5432, "127.0.0.1:5432", "localhost:5432" -auth.pgsql.server = "127.0.0.1:5432" - -## PostgreSQL pool size. -## -## Value: Number -auth.pgsql.pool = 8 - -## PostgreSQL username. -## -## Value: String -auth.pgsql.username = root - -## PostgreSQL password. -## -## Value: String -#auth.pgsql.password = - -## PostgreSQL database. -## -## Value: String -auth.pgsql.database = mqtt - -## PostgreSQL database encoding. -## -## Value: String -auth.pgsql.encoding = utf8 - -## Whether to enable SSL connection. -## -## Value: on | off -auth.pgsql.ssl.enable = off - -## TLS version. -## -## Available enum values: -## tlsv1.3,tlsv1.2,tlsv1.1,tlsv1 -## -## Value: String, seperated by ',' -#auth.pgsql.ssl.tls_versions = tlsv1.3,tlsv1.2,tlsv1.1 - -## SSL keyfile. -## -## Value: File -#auth.pgsql.ssl.keyfile = - -## SSL certfile. -## -## Value: File -#auth.pgsql.ssl.certfile = - -## SSL cacertfile. -## -## Value: File -#auth.pgsql.ssl.cacertfile = - -## In mode verify_none the default behavior is to allow all x509-path -## validation errors. -## -## Value: true | false -#auth.pgsql.ssl.verify = false - -## If not specified, the server's names returned in server's certificate is validated against -## what's provided `auth.pgsql.server` config's host part. -## Setting to 'disable' will make EMQ X ignore unmatched server names. -## If set with a host name, the server's names returned in server's certificate is validated -## against this value. -## -## Value: String | disable -## auth.pgsql.ssl.server_name_indication = disable - -## Authentication query. -## -## Value: SQL -## -## Variables: -## - %u: username -## - %c: clientid -## - %C: common name of client TLS cert -## - %d: subject of client TLS cert -## -auth.pgsql.auth_query = "select password from mqtt_user where username = '%u' limit 1" - -## Password hash. -## -## Value: plain | md5 | sha | sha256 | bcrypt -auth.pgsql.password_hash = sha256 - -## sha256 with salt prefix -## auth.pgsql.password_hash = "salt,sha256" - -## sha256 with salt suffix -## auth.pgsql.password_hash = "sha256,salt" - -## bcrypt with salt prefix -## auth.pgsql.password_hash = "salt,bcrypt" - -## pbkdf2 with macfun iterations dklen -## macfun: md4, md5, ripemd160, sha, sha224, sha256, sha384, sha512 -## auth.pgsql.password_hash = "pbkdf2,sha256,1000,20" - -## Superuser query. -## -## Value: SQL -## -## Variables: -## - %u: username -## - %c: clientid -## - %C: common name of client TLS cert -## - %d: subject of client TLS cert -## -auth.pgsql.super_query = "select is_superuser from mqtt_user where username = '%u' limit 1" - -## ACL query. Comment this query, the ACL will be disabled. -## -## Value: SQL -## -## Variables: -## - %a: ipaddress -## - %u: username -## - %c: clientid -## -## Note: You can add the 'ORDER BY' statement to control the rules match order -auth.pgsql.acl_query = "select allow, ipaddr, username, clientid, access, topic from mqtt_acl where ipaddr = '%a' or username = '%u' or username = '$all' or clientid = '%c'" diff --git a/apps/emqx_auth_pgsql/include/emqx_auth_pgsql.hrl b/apps/emqx_auth_pgsql/include/emqx_auth_pgsql.hrl deleted file mode 100644 index b86692752..000000000 --- a/apps/emqx_auth_pgsql/include/emqx_auth_pgsql.hrl +++ /dev/null @@ -1,23 +0,0 @@ --define(APP, emqx_auth_pgsql). - --record(auth_metrics, { - success = 'client.auth.success', - failure = 'client.auth.failure', - ignore = 'client.auth.ignore' - }). - --record(acl_metrics, { - allow = 'client.acl.allow', - deny = 'client.acl.deny', - ignore = 'client.acl.ignore' - }). - --define(METRICS(Type), tl(tuple_to_list(#Type{}))). --define(METRICS(Type, K), #Type{}#Type.K). - --define(AUTH_METRICS, ?METRICS(auth_metrics)). --define(AUTH_METRICS(K), ?METRICS(auth_metrics, K)). - --define(ACL_METRICS, ?METRICS(acl_metrics)). --define(ACL_METRICS(K), ?METRICS(acl_metrics, K)). - diff --git a/apps/emqx_auth_pgsql/mqtt.sql b/apps/emqx_auth_pgsql/mqtt.sql deleted file mode 100644 index 933b0058a..000000000 --- a/apps/emqx_auth_pgsql/mqtt.sql +++ /dev/null @@ -1,28 +0,0 @@ - -CREATE TABLE mqtt_user ( - id SERIAL primary key, - is_superuser boolean, - username character varying(100), - password character varying(100), - salt character varying(40) -); - -CREATE TABLE mqtt_acl ( - id SERIAL primary key, - allow integer, - ipaddr character varying(60), - username character varying(100), - clientid character varying(100), - access integer, - topic character varying(100) -); - -INSERT INTO mqtt_acl (id, allow, ipaddr, username, clientid, access, topic) -VALUES - (1,1,NULL,'$all',NULL,2,'#'), - (2,0,NULL,'$all',NULL,1,'$SYS/#'), - (3,0,NULL,'$all',NULL,1,'eq #'), - (5,1,'127.0.0.1',NULL,NULL,2,'$SYS/#'), - (6,1,'127.0.0.1',NULL,NULL,2,'#'), - (7,1,NULL,'dashboard',NULL,1,'$SYS/#'); - diff --git a/apps/emqx_auth_pgsql/priv/emqx_auth_pgsql.schema b/apps/emqx_auth_pgsql/priv/emqx_auth_pgsql.schema deleted file mode 100644 index 2be9b0670..000000000 --- a/apps/emqx_auth_pgsql/priv/emqx_auth_pgsql.schema +++ /dev/null @@ -1,184 +0,0 @@ -%%-*- mode: erlang -*- -%% emqx_auth_pgsl config mapping - -{mapping, "auth.pgsql.server", "emqx_auth_pgsql.server", [ - {default, {"127.0.0.1", 5432}}, - {datatype, [integer, ip, string]} -]}. - -{mapping, "auth.pgsql.pool", "emqx_auth_pgsql.server", [ - {default, 8}, - {datatype, integer} -]}. - -{mapping, "auth.pgsql.database", "emqx_auth_pgsql.server", [ - {datatype, string} -]}. - -{mapping, "auth.pgsql.username", "emqx_auth_pgsql.server", [ - {default, ""}, - {datatype, string} -]}. - -{mapping, "auth.pgsql.password", "emqx_auth_pgsql.server", [ - {default, ""}, - {datatype, string} -]}. - -{mapping, "auth.pgsql.encoding", "emqx_auth_pgsql.server", [ - {default, utf8}, - {datatype, atom} -]}. - -{mapping, "auth.pgsql.ssl.enable", "emqx_auth_pgsql.server", [ - {default, off}, - {datatype, {enum, [on, off, true, false]}} %% FIXME: true/fasle is compatible with 4.0-4.2 version format, plan to delete in 5.0 -]}. - -{mapping, "auth.pgsql.ssl.tls_versions", "emqx_auth_pgsql.server", [ - {default, "tlsv1.3,tlsv1.2,tlsv1.1"}, - {datatype, string} -]}. - -{mapping, "auth.pgsql.ssl.keyfile", "emqx_auth_pgsql.server", [ - {datatype, string} -]}. - -{mapping, "auth.pgsql.ssl.certfile", "emqx_auth_pgsql.server", [ - {datatype, string} -]}. - -{mapping, "auth.pgsql.ssl.cacertfile", "emqx_auth_pgsql.server", [ - {datatype, string} -]}. - -{mapping, "auth.pgsql.ssl.verify", "emqx_auth_pgsql.server", [ - {default, false}, - {datatype, {enum, [true, false]}} -]}. - -{mapping, "auth.pgsql.ssl.server_name_indication", "emqx_auth_pgsql.server", [ - {datatype, string} -]}. - -%% FIXME: compatible with 4.0-4.2 version format, plan to delete in 5.0 -{mapping, "auth.pgsql.ssl_opts.keyfile", "emqx_auth_pgsql.server", [ - {datatype, string} -]}. - -%% FIXME: compatible with 4.0-4.2 version format, plan to delete in 5.0 -{mapping, "auth.pgsql.ssl_opts.certfile", "emqx_auth_pgsql.server", [ - {datatype, string} -]}. - -%% FIXME: compatible with 4.0-4.2 version format, plan to delete in 5.0 -{mapping, "auth.pgsql.ssl_opts.cacertfile", "emqx_auth_pgsql.server", [ - {datatype, string} -]}. - -%% FIXME: compatible with 4.0-4.2 version format, plan to delete in 5.0 -{mapping, "auth.pgsql.ssl_opts.tls_versions", "emqx_auth_pgsql.server", [ - {default, "tlsv1.2"}, - {datatype, string} -]}. - -{translation, "emqx_auth_pgsql.server", fun(Conf) -> - {PgHost, PgPort} = - case cuttlefish:conf_get("auth.pgsql.server", Conf) of - {Ip, Port} -> {Ip, Port}; - S -> case string:tokens(S, ":") of - [Domain] -> {Domain, 5432}; - [Domain, Port] -> {Domain, list_to_integer(Port)} - end - end, - Pool = cuttlefish:conf_get("auth.pgsql.pool", Conf), - Username = cuttlefish:conf_get("auth.pgsql.username", Conf), - Passwd = cuttlefish:conf_get("auth.pgsql.password", Conf, ""), - DB = cuttlefish:conf_get("auth.pgsql.database", Conf), - Encoding = cuttlefish:conf_get("auth.pgsql.encoding", Conf), - - Filter = fun(Opts) -> [{K, V} || {K, V} <- Opts, V =/= undefined] end, - SslOpts = fun(Prefix) -> - Verify = case cuttlefish:conf_get(Prefix ++ ".verify", Conf, false) of - true -> verify_peer; - false -> verify_none - end, - Filter([{keyfile, cuttlefish:conf_get(Prefix ++ ".keyfile", Conf, undefined)}, - {certfile, cuttlefish:conf_get(Prefix ++ ".certfile", Conf, undefined)}, - {cacertfile, cuttlefish:conf_get(Prefix ++ ".cacertfile", Conf, undefined)}, - {verify, Verify}, - {server_name_indication, case cuttlefish:conf_get(Prefix ++ ".server_name_indication", Conf, undefined) of - "disable" -> disable; - SNI -> SNI - end}, - {versions, [list_to_existing_atom(Value) - || Value <- string:tokens(cuttlefish:conf_get(Prefix ++ ".tls_versions", Conf), " ,")]}]) - end, - - %% FIXME: compatible with 4.0-4.2 version format, plan to delete in 5.0 - GenSsl = case cuttlefish:conf_get("auth.pgsql.ssl.cacertfile", Conf, undefined) of - undefined -> [{ssl, true}, {ssl_opts, SslOpts("auth.pgsql.ssl_opts")}]; - _ -> [{ssl, true}, {ssl_opts, SslOpts("auth.pgsql.ssl")}] - end, - - %% FIXME: compatible with 4.0-4.2 version format, plan to delete in 5.0 - Ssl = case cuttlefish:conf_get("auth.pgsql.ssl.enable", Conf) of - on -> GenSsl; - off -> []; - true -> [{ssl, true}, {ssl_opts, SslOpts("auth.pgsql.ssl_opts")}]; - false -> [] - end, - - TempHost = case inet:parse_address(PgHost) of - {ok, IpAddr} -> - IpAddr; - _ -> - PgHost - end, - [{pool_size, Pool}, - {auto_reconnect, 1}, - {host, TempHost}, - {port, PgPort}, - {username, Username}, - {password, Passwd}, - {database, DB}, - {encoding, Encoding}] ++ Ssl -end}. - -{mapping, "auth.pgsql.auth_query", "emqx_auth_pgsql.auth_query", [ - {datatype, string} -]}. - -{mapping, "auth.pgsql.password_hash", "emqx_auth_pgsql.password_hash", [ - {datatype, string} -]}. - -{mapping, "auth.pgsql.pbkdf2_macfun", "emqx_auth_pgsql.pbkdf2_macfun", [ - {datatype, atom} -]}. - -{mapping, "auth.pgsql.pbkdf2_iterations", "emqx_auth_pgsql.pbkdf2_iterations", [ - {datatype, integer} -]}. - -{mapping, "auth.pgsql.pbkdf2_dklen", "emqx_auth_pgsql.pbkdf2_dklen", [ - {datatype, integer} -]}. - -{mapping, "auth.pgsql.super_query", "emqx_auth_pgsql.super_query", [ - {datatype, string} -]}. - -{mapping, "auth.pgsql.acl_query", "emqx_auth_pgsql.acl_query", [ - {datatype, string} -]}. - -{translation, "emqx_auth_pgsql.password_hash", fun(Conf) -> - HashValue = cuttlefish:conf_get("auth.pgsql.password_hash", Conf), - case string:tokens(HashValue, ",") of - [Hash] -> list_to_atom(Hash); - [Prefix, Suffix] -> {list_to_atom(Prefix), list_to_atom(Suffix)}; - [Hash, MacFun, Iterations, Dklen] -> {list_to_atom(Hash), list_to_atom(MacFun), list_to_integer(Iterations), list_to_integer(Dklen)}; - _ -> plain - end -end}. diff --git a/apps/emqx_auth_pgsql/rebar.config b/apps/emqx_auth_pgsql/rebar.config deleted file mode 100644 index 3155bbef3..000000000 --- a/apps/emqx_auth_pgsql/rebar.config +++ /dev/null @@ -1,21 +0,0 @@ -{deps, - [{epgsql, {git, "https://github.com/epgsql/epgsql", {tag, "4.4.0"}}} - ]}. - -{erl_opts, [warn_unused_vars, - warn_shadow_vars, - warn_unused_import, - warn_obsolete_guard, - debug_info, - compressed - ]}. -{overrides, [{add, [{erl_opts, [compressed]}]}]}. - -{xref_checks, [undefined_function_calls, undefined_functions, - locals_not_used, deprecated_function_calls, - warnings_as_errors, deprecated_functions - ]}. - -{cover_enabled, true}. -{cover_opts, [verbose]}. -{cover_export_enabled, true}. diff --git a/apps/emqx_auth_pgsql/src/emqx_acl_pgsql.erl b/apps/emqx_auth_pgsql/src/emqx_acl_pgsql.erl deleted file mode 100644 index 099ce4438..000000000 --- a/apps/emqx_auth_pgsql/src/emqx_acl_pgsql.erl +++ /dev/null @@ -1,117 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_acl_pgsql). - --include("emqx_auth_pgsql.hrl"). --include_lib("emqx/include/emqx.hrl"). --include_lib("emqx/include/logger.hrl"). - -%% ACL callbacks --export([ register_metrics/0 - , check_acl/5 - , description/0 - ]). - --spec(register_metrics() -> ok). -register_metrics() -> - lists:foreach(fun emqx_metrics:ensure/1, ?ACL_METRICS). - -check_acl(ClientInfo, PubSub, Topic, NoMatchAction, #{pool := Pool} = State) -> - case do_check_acl(Pool, ClientInfo, PubSub, Topic, NoMatchAction, State) of - ok -> emqx_metrics:inc(?ACL_METRICS(ignore)), ok; - {stop, allow} -> emqx_metrics:inc(?ACL_METRICS(allow)), {stop, allow}; - {stop, deny} -> emqx_metrics:inc(?ACL_METRICS(deny)), {stop, deny} - end. - -do_check_acl(_Pool, #{username := <<$$, _/binary>>}, _PubSub, _Topic, _NoMatchAction, _State) -> - ok; -do_check_acl(Pool, ClientInfo, PubSub, Topic, _NoMatchAction, #{acl_query := {AclSql, AclParams}}) -> - case emqx_auth_pgsql_cli:equery(Pool, AclSql, AclParams, ClientInfo) of - {ok, _, []} -> ok; - {ok, _, Rows} -> - Rules = filter(PubSub, compile(Rows)), - case match(ClientInfo, Topic, Rules) of - {matched, allow} -> {stop, allow}; - {matched, deny} -> {stop, deny}; - nomatch -> ok - end; - {error, Reason} -> - ?LOG(error, "[Postgres] do_check_acl error: ~p~n", [Reason]), - ok - end. - -match(_ClientInfo, _Topic, []) -> - nomatch; - -match(ClientInfo, Topic, [Rule|Rules]) -> - case emqx_access_rule:match(ClientInfo, Topic, Rule) of - nomatch -> match(ClientInfo, Topic, Rules); - {matched, AllowDeny} -> {matched, AllowDeny} - end. - -filter(PubSub, Rules) -> - [Term || Term = {_, _, Access, _} <- Rules, - Access =:= PubSub orelse Access =:= pubsub]. - -compile(Rows) -> - compile(Rows, []). -compile([], Acc) -> - Acc; -compile([{Allow, IpAddr, Username, ClientId, Access, Topic}|T], Acc) -> - Who = who(IpAddr, Username, ClientId), - Term = {allow(Allow), Who, access(Access), [topic(Topic)]}, - compile(T, [emqx_access_rule:compile(Term) | Acc]). - -who(_, <<"$all">>, _) -> - all; -who(null, null, null) -> - throw(undefined_who); -who(CIDR, Username, ClientId) -> - Cols = [{ipaddr, b2l(CIDR)}, {user, Username}, {client, ClientId}], - case [{C, V} || {C, V} <- Cols, not empty(V)] of - [Who] -> Who; - Conds -> {'and', Conds} - end. - -allow(1) -> allow; -allow(0) -> deny; -allow(<<"1">>) -> allow; -allow(<<"0">>) -> deny. - -access(1) -> subscribe; -access(2) -> publish; -access(3) -> pubsub; -access(<<"1">>) -> subscribe; -access(<<"2">>) -> publish; -access(<<"3">>) -> pubsub. - -topic(<<"eq ", Topic/binary>>) -> - {eq, Topic}; -topic(Topic) -> - Topic. - -description() -> - "ACL with Postgres". - -b2l(null) -> null; -b2l(B) -> binary_to_list(B). - -empty(null) -> true; -empty("") -> true; -empty(<<>>) -> true; -empty(_) -> false. - diff --git a/apps/emqx_auth_pgsql/src/emqx_auth_pgsql.app.src b/apps/emqx_auth_pgsql/src/emqx_auth_pgsql.app.src deleted file mode 100644 index e97487e21..000000000 --- a/apps/emqx_auth_pgsql/src/emqx_auth_pgsql.app.src +++ /dev/null @@ -1,14 +0,0 @@ -{application, emqx_auth_pgsql, - [{description, "EMQ X Authentication/ACL with PostgreSQL"}, - {vsn, "4.4.0"}, % strict semver, bump manually! - {modules, []}, - {registered, [emqx_auth_pgsql_sup]}, - {applications, [kernel,stdlib,epgsql,ecpool]}, - {mod, {emqx_auth_pgsql_app,[]}}, - {env, []}, - {licenses, ["Apache-2.0"]}, - {maintainers, ["EMQ X Team "]}, - {links, [{"Homepage", "https://emqx.io/"}, - {"Github", "https://github.com/emqx/emqx-auth-pgsql"} - ]} - ]}. diff --git a/apps/emqx_auth_pgsql/src/emqx_auth_pgsql.erl b/apps/emqx_auth_pgsql/src/emqx_auth_pgsql.erl deleted file mode 100644 index f8f365cd7..000000000 --- a/apps/emqx_auth_pgsql/src/emqx_auth_pgsql.erl +++ /dev/null @@ -1,91 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_auth_pgsql). - --include("emqx_auth_pgsql.hrl"). - --include_lib("emqx/include/emqx.hrl"). --include_lib("emqx/include/logger.hrl"). - --export([ register_metrics/0 - , check/3 - , description/0 - ]). - --spec(register_metrics() -> ok). -register_metrics() -> - lists:foreach(fun emqx_metrics:ensure/1, ?AUTH_METRICS). - -%%-------------------------------------------------------------------- -%% Auth Module Callbacks -%%-------------------------------------------------------------------- - -check(ClientInfo = #{password := Password}, AuthResult, - #{auth_query := {AuthSql, AuthParams}, - super_query := SuperQuery, - hash_type := HashType, - pool := Pool}) -> - CheckPass = case emqx_auth_pgsql_cli:equery(Pool, AuthSql, AuthParams, ClientInfo) of - {ok, _, [Record]} -> - check_pass(erlang:append_element(Record, Password), HashType); - {ok, _, []} -> - {error, not_found}; - {error, Reason} -> - ?LOG(error, "[Postgres] query '~p' failed: ~p", [AuthSql, Reason]), - {error, not_found} - end, - case CheckPass of - ok -> - emqx_metrics:inc(?AUTH_METRICS(success)), - {stop, AuthResult#{is_superuser => is_superuser(Pool, SuperQuery, ClientInfo), - anonymous => false, - auth_result => success}}; - {error, not_found} -> - emqx_metrics:inc(?AUTH_METRICS(ignore)), ok; - {error, ResultCode} -> - ?LOG(error, "[Postgres] Auth from pgsql failed: ~p", [ResultCode]), - emqx_metrics:inc(?AUTH_METRICS(failure)), - {stop, AuthResult#{auth_result => ResultCode, anonymous => false}} - end. - -%%-------------------------------------------------------------------- -%% Is Superuser? -%%-------------------------------------------------------------------- - --spec(is_superuser(atom(),undefined | {string(), list()}, emqx_types:client()) -> boolean()). -is_superuser(_Pool, undefined, _Client) -> - false; -is_superuser(Pool, {SuperSql, Params}, ClientInfo) -> - case emqx_auth_pgsql_cli:equery(Pool, SuperSql, Params, ClientInfo) of - {ok, [_Super], [{true}]} -> - true; - {ok, [_Super], [_False]} -> - false; - {ok, [_Super], []} -> - false; - {error, _Error} -> - false - end. - -check_pass(Password, HashType) -> - case emqx_passwd:check_pass(Password, HashType) of - ok -> ok; - {error, _Reason} -> {error, not_authorized} - end. - -description() -> "Authentication with PostgreSQL". - diff --git a/apps/emqx_auth_pgsql/src/emqx_auth_pgsql_app.erl b/apps/emqx_auth_pgsql/src/emqx_auth_pgsql_app.erl deleted file mode 100644 index 571fd0c4b..000000000 --- a/apps/emqx_auth_pgsql/src/emqx_auth_pgsql_app.erl +++ /dev/null @@ -1,63 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_auth_pgsql_app). - --behaviour(application). - --emqx_plugin(auth). - --include("emqx_auth_pgsql.hrl"). - --import(emqx_auth_pgsql_cli, [parse_query/2]). - -%% Application callbacks --export([ start/2 - , stop/1 - ]). - -%%-------------------------------------------------------------------- -%% Application callbacks -%%-------------------------------------------------------------------- - -start(_StartType, _StartArgs) -> - {ok, Sup} = emqx_auth_pgsql_sup:start_link(), - if_enabled(auth_query, fun(AuthQuery) -> - SuperQuery = parse_query(super_query, application:get_env(?APP, super_query, undefined)), - {ok, HashType} = application:get_env(?APP, password_hash), - AuthEnv = #{auth_query => AuthQuery, - super_query => SuperQuery, - hash_type => HashType, - pool => ?APP}, - ok = emqx_auth_pgsql:register_metrics(), - ok = emqx:hook('client.authenticate', {emqx_auth_pgsql, check, [AuthEnv]}) - end), - if_enabled(acl_query, fun(AclQuery) -> - ok = emqx_acl_pgsql:register_metrics(), - ok = emqx:hook('client.check_acl', {emqx_acl_pgsql, check_acl, [#{acl_query => AclQuery, pool => ?APP}]}) - end), - {ok, Sup}. - -stop(_State) -> - ok = emqx:unhook('client.authenticate', {emqx_auth_pgsql, check}), - ok = emqx:unhook('client.check_acl', {emqx_acl_pgsql, check_acl}). - -if_enabled(Par, Fun) -> - case application:get_env(?APP, Par) of - {ok, Query} -> Fun(parse_query(Par, Query)); - undefined -> ok - end. - diff --git a/apps/emqx_auth_pgsql/src/emqx_auth_pgsql_cli.erl b/apps/emqx_auth_pgsql/src/emqx_auth_pgsql_cli.erl deleted file mode 100644 index 7dde566a2..000000000 --- a/apps/emqx_auth_pgsql/src/emqx_auth_pgsql_cli.erl +++ /dev/null @@ -1,150 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_auth_pgsql_cli). - --behaviour(ecpool_worker). - --include("emqx_auth_pgsql.hrl"). - --include_lib("emqx/include/emqx.hrl"). --include_lib("emqx/include/logger.hrl"). - --export([connect/1]). --export([parse_query/2]). --export([ equery/4 - , equery/3 - ]). - --type client_info() :: #{username := _, - clientid := _, - peerhost := _, - _ => _}. - -%%-------------------------------------------------------------------- -%% Avoid SQL Injection: Parse SQL to Parameter Query. -%%-------------------------------------------------------------------- - -parse_query(_Par, undefined) -> - undefined; -parse_query(Par, Sql) -> - case re:run(Sql, "'%[ucCad]'", [global, {capture, all, list}]) of - {match, Variables} -> - Params = [Var || [Var] <- Variables], - {atom_to_list(Par), Params}; - nomatch -> - {atom_to_list(Par), []} - end. - -pgvar(Sql, Params) -> - Vars = ["$" ++ integer_to_list(I) || I <- lists:seq(1, length(Params))], - lists:foldl(fun({Param, Var}, S) -> - re:replace(S, Param, Var, [{return, list}]) - end, Sql, lists:zip(Params, Vars)). - -%%-------------------------------------------------------------------- -%% PostgreSQL Connect/Query -%%-------------------------------------------------------------------- - -%% Due to a bug in epgsql the caluse for `econnrefused` is not recognised by -%% dialyzer, result in this error: -%% The pattern {'error', Reason = 'econnrefused'} can never match the type ... -%% https://github.com/epgsql/epgsql/issues/246 --dialyzer([{nowarn_function, [connect/1]}]). -connect(Opts) -> - Host = proplists:get_value(host, Opts), - Username = proplists:get_value(username, Opts), - Password = proplists:get_value(password, Opts), - case epgsql:connect(Host, Username, Password, conn_opts(Opts)) of - {ok, C} -> - conn_post(C), - {ok, C}; - {error, Reason = econnrefused} -> - ?LOG(error, "[Postgres] Can't connect to Postgres server: Connection refused."), - {error, Reason}; - {error, Reason = invalid_authorization_specification} -> - ?LOG(error, "[Postgres] Can't connect to Postgres server: Invalid authorization specification."), - {error, Reason}; - {error, Reason = invalid_password} -> - ?LOG(error, "[Postgres] Can't connect to Postgres server: Invalid password."), - {error, Reason}; - {error, Reason} -> - ?LOG(error, "[Postgres] Can't connect to Postgres server: ~p", [Reason]), - {error, Reason} - end. - -conn_post(Connection) -> - lists:foreach(fun(Par) -> - Sql0 = application:get_env(?APP, Par, undefined), - case parse_query(Par, Sql0) of - undefined -> ok; - {_, Params} -> - Sql = pgvar(Sql0, Params), - epgsql:parse(Connection, atom_to_list(Par), Sql, []) - end - end, [auth_query, acl_query, super_query]). - -conn_opts(Opts) -> - conn_opts(Opts, []). -conn_opts([], Acc) -> - Acc; -conn_opts([Opt = {database, _}|Opts], Acc) -> - conn_opts(Opts, [Opt|Acc]); -conn_opts([Opt = {ssl, _}|Opts], Acc) -> - conn_opts(Opts, [Opt|Acc]); -conn_opts([Opt = {port, _}|Opts], Acc) -> - conn_opts(Opts, [Opt|Acc]); -conn_opts([Opt = {timeout, _}|Opts], Acc) -> - conn_opts(Opts, [Opt|Acc]); -conn_opts([Opt = {ssl_opts, _}|Opts], Acc) -> - conn_opts(Opts, [Opt|Acc]); -conn_opts([_Opt|Opts], Acc) -> - conn_opts(Opts, Acc). - --spec(equery(atom(), string() | epgsql:statement(), Parameters::[any()]) -> {ok, ColumnsDescription :: [any()], RowsValues :: [any()]} | {error, any()} ). -equery(Pool, Sql, Params) -> - ecpool:with_client(Pool, fun(C) -> epgsql:prepared_query(C, Sql, Params) end). - --spec(equery(atom(), string() | epgsql:statement(), Parameters::[any()], client_info()) -> {ok, ColumnsDescription :: [any()], RowsValues :: [any()]} | {error, any()} ). -equery(Pool, Sql, Params, ClientInfo) -> - ecpool:with_client(Pool, fun(C) -> epgsql:prepared_query(C, Sql, replvar(Params, ClientInfo)) end). - -replvar(Params, ClientInfo) -> - replvar(Params, ClientInfo, []). - -replvar([], _ClientInfo, Acc) -> - lists:reverse(Acc); - -replvar(["'%u'" | Params], ClientInfo = #{username := Username}, Acc) -> - replvar(Params, ClientInfo, [Username | Acc]); -replvar(["'%c'" | Params], ClientInfo = #{clientid := ClientId}, Acc) -> - replvar(Params, ClientInfo, [ClientId | Acc]); -replvar(["'%a'" | Params], ClientInfo = #{peerhost := IpAddr}, Acc) -> - replvar(Params, ClientInfo, [inet_parse:ntoa(IpAddr) | Acc]); -replvar(["'%C'" | Params], ClientInfo, Acc) -> - replvar(Params, ClientInfo, [safe_get(cn, ClientInfo)| Acc]); -replvar(["'%d'" | Params], ClientInfo, Acc) -> - replvar(Params, ClientInfo, [safe_get(dn, ClientInfo)| Acc]); -replvar([Param | Params], ClientInfo, Acc) -> - replvar(Params, ClientInfo, [Param | Acc]). - -safe_get(K, ClientInfo) -> - bin(maps:get(K, ClientInfo, undefined)). - -bin(A) when is_atom(A) -> atom_to_binary(A, utf8); -bin(B) when is_binary(B) -> B; -bin(X) -> X. - diff --git a/apps/emqx_auth_pgsql/src/emqx_auth_pgsql_sup.erl b/apps/emqx_auth_pgsql/src/emqx_auth_pgsql_sup.erl deleted file mode 100644 index 21d005dc2..000000000 --- a/apps/emqx_auth_pgsql/src/emqx_auth_pgsql_sup.erl +++ /dev/null @@ -1,37 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_auth_pgsql_sup). - --behaviour(supervisor). - --include("emqx_auth_pgsql.hrl"). - -%% API --export([start_link/0]). - -%% Supervisor callbacks --export([init/1]). - -start_link() -> - supervisor:start_link({local, ?MODULE}, ?MODULE, []). - -init([]) -> - %% PgSQL Connection Pool - {ok, Opts} = application:get_env(?APP, server), - PoolSpec = ecpool:pool_spec(?APP, ?APP, emqx_auth_pgsql_cli, Opts), - {ok, {{one_for_one, 10, 100}, [PoolSpec]}}. - diff --git a/apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE.erl b/apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE.erl deleted file mode 100644 index 6c4cd2eb3..000000000 --- a/apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE.erl +++ /dev/null @@ -1,221 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_auth_pgsql_SUITE). - --compile(export_all). --compile(nowarn_export_all). - --define(POOL, emqx_auth_pgsql). - --define(APP, emqx_auth_pgsql). - --include_lib("emqx/include/emqx.hrl"). - --include_lib("eunit/include/eunit.hrl"). - --include_lib("common_test/include/ct.hrl"). - -%%setp1 init table --define(DROP_ACL_TABLE, "DROP TABLE IF EXISTS mqtt_acl"). - --define(CREATE_ACL_TABLE, "CREATE TABLE mqtt_acl ( - id SERIAL primary key, - allow integer, - ipaddr character varying(60), - username character varying(100), - clientid character varying(100), - access integer, - topic character varying(100))"). - --define(INIT_ACL, "INSERT INTO mqtt_acl (id, allow, ipaddr, username, clientid, access, topic) - VALUES - (1,1,'127.0.0.1','u1','c1',1,'t1'), - (2,0,'127.0.0.1','u2','c2',1,'t1'), - (3,1,'10.10.0.110','u1','c1',1,'t1'), - (4,1,'127.0.0.1','u3','c3',3,'t1')"). - --define(DROP_AUTH_TABLE, "DROP TABLE IF EXISTS mqtt_user"). - --define(CREATE_AUTH_TABLE, "CREATE TABLE mqtt_user ( - id SERIAL primary key, - is_superuser boolean, - username character varying(100), - password character varying(100), - salt character varying(40))"). - --define(INIT_AUTH, "INSERT INTO mqtt_user (id, is_superuser, username, password, salt) - VALUES - (1, true, 'plain', 'plain', 'salt'), - (2, false, 'md5', '1bc29b36f623ba82aaf6724fd3b16718', 'salt'), - (3, false, 'sha', 'd8f4590320e1343a915b6394170650a8f35d6926', 'salt'), - (4, false, 'sha256', '5d5b09f6dcb2d53a5fffc60c4ac0d55fabdf556069d6631545f42aa6e3500f2e', 'salt'), - (5, false, 'pbkdf2_password', 'cdedb5281bb2f801565a1122b2563515', 'ATHENA.MIT.EDUraeburn'), - (6, false, 'bcrypt_foo', '$2a$12$sSS8Eg.ovVzaHzi1nUHYK.HbUIOdlQI0iS22Q5rd5z.JVVYH6sfm6', '$2a$12$sSS8Eg.ovVzaHzi1nUHYK.'), - (7, false, 'bcrypt', '$2y$16$rEVsDarhgHYB0TGnDFJzyu5f.T.Ha9iXMTk9J36NCMWWM7O16qyaK', 'salt')"). - -all() -> - emqx_ct:all(?MODULE). - -init_per_suite(Config) -> - emqx_ct_helpers:start_apps([emqx_auth_pgsql]), - drop_acl(), - drop_auth(), - init_auth(), - init_acl(), - set_special_configs(), - Config. - -end_per_suite(Config) -> - emqx_ct_helpers:stop_apps([emqx_auth_pgsql]), - Config. - -set_special_configs() -> - application:set_env(emqx, acl_nomatch, deny), - application:set_env(emqx, allow_anonymous, false), - application:set_env(emqx, enable_acl_cache, false). - -t_comment_config(_) -> - AuthCount = length(emqx_hooks:lookup('client.authenticate')), - AclCount = length(emqx_hooks:lookup('client.check_acl')), - application:stop(?APP), - [application:unset_env(?APP, Par) || Par <- [acl_query, auth_query]], - application:start(?APP), - ?assertEqual([], emqx_hooks:lookup('client.authenticate')), - ?assertEqual(AuthCount - 1, length(emqx_hooks:lookup('client.authenticate'))), - ?assertEqual(AclCount - 1, length(emqx_hooks:lookup('client.check_acl'))). - -t_placeholders(_) -> - ClientA = #{username => <<"plain">>, clientid => <<"plain">>, zone => external}, - reload([{password_hash, plain}, - {auth_query, "select password from mqtt_user where username = '%u' and 'a_cn_val' = '%C' limit 1"}]), - {error, not_authorized} = - emqx_access_control:authenticate(ClientA#{password => <<"plain">>}), - {error, not_authorized} = - emqx_access_control:authenticate(ClientA#{password => <<"plain">>, cn => undefined}), - {ok, _} = - emqx_access_control:authenticate(ClientA#{password => <<"plain">>, cn => <<"a_cn_val">>}), - - reload([{auth_query, "select password from mqtt_user where username = '%c' and 'a_dn_val' = '%d' limit 1"}]), - {error, not_authorized} = - emqx_access_control:authenticate(ClientA#{password => <<"plain">>}), - {error, not_authorized} = - emqx_access_control:authenticate(ClientA#{password => <<"plain">>, dn => undefined}), - {ok, _} = - emqx_access_control:authenticate(ClientA#{password => <<"plain">>, dn => <<"a_dn_val">>}), - - reload([{auth_query, "select password from mqtt_user where username = '%u' and '192.168.1.5' = '%a' limit 1"}]), - {error, not_authorized} = - emqx_access_control:authenticate(ClientA#{password => <<"plain">>}), - {ok, _} = - emqx_access_control:authenticate(ClientA#{password => <<"plain">>, peerhost => {192,168,1,5}}). - -t_check_auth(_) -> - Plain = #{clientid => <<"client1">>, username => <<"plain">>, zone => external}, - Md5 = #{clientid => <<"md5">>, username => <<"md5">>, zone => external}, - Sha = #{clientid => <<"sha">>, username => <<"sha">>, zone => external}, - Sha256 = #{clientid => <<"sha256">>, username => <<"sha256">>, zone => external}, - Pbkdf2 = #{clientid => <<"pbkdf2_password">>, username => <<"pbkdf2_password">>, zone => external}, - BcryptFoo = #{clientid => <<"bcrypt_foo">>, username => <<"bcrypt_foo">>, zone => external}, - User1 = #{clientid => <<"bcrypt_foo">>, username => <<"user">>, zone => external}, - Bcrypt = #{clientid => <<"bcrypt">>, username => <<"bcrypt">>, zone => external}, - BcryptWrong = #{clientid => <<"bcrypt_wrong">>, username => <<"bcrypt_wrong">>, zone => external}, - reload([{password_hash, plain}]), - {ok,#{is_superuser := true}} = - emqx_access_control:authenticate(Plain#{password => <<"plain">>}), - reload([{password_hash, md5}]), - {ok,#{is_superuser := false}} = - emqx_access_control:authenticate(Md5#{password => <<"md5">>}), - reload([{password_hash, sha}]), - {ok,#{is_superuser := false}} = - emqx_access_control:authenticate(Sha#{password => <<"sha">>}), - reload([{password_hash, sha256}]), - {ok,#{is_superuser := false}} = - emqx_access_control:authenticate(Sha256#{password => <<"sha256">>}), - reload([{password_hash, bcrypt}]), - {ok,#{is_superuser := false}} = - emqx_access_control:authenticate(Bcrypt#{password => <<"password">>}), - {error, not_authorized} = - emqx_access_control:authenticate(BcryptWrong#{password => <<"password">>}), - %%pbkdf2 sha - reload([{password_hash, {pbkdf2, sha, 1, 16}}, - {auth_query, "select password, salt from mqtt_user where username = '%u' limit 1"}]), - {ok,#{is_superuser := false}} = - emqx_access_control:authenticate(Pbkdf2#{password => <<"password">>}), - reload([{password_hash, {salt, bcrypt}}]), - {ok,#{is_superuser := false}} = - emqx_access_control:authenticate(BcryptFoo#{password => <<"foo">>}), - {error, _} = emqx_access_control:authenticate(User1#{password => <<"foo">>}), - {error, not_authorized} = emqx_access_control:authenticate(Bcrypt#{password => <<"password">>}). - -t_check_acl(_) -> - User1 = #{zone => external, peerhost => {127,0,0,1}, clientid => <<"c1">>, username => <<"u1">>}, - User2 = #{zone => external, peerhost => {127,0,0,1}, clientid => <<"c2">>, username => <<"u2">>}, - allow = emqx_access_control:check_acl(User1, subscribe, <<"t1">>), - deny = emqx_access_control:check_acl(User2, subscribe, <<"t1">>), - User3 = #{zone => external, peerhost => {10,10,0,110}, clientid => <<"c1">>, username => <<"u1">>}, - User4 = #{zone => external, peerhost => {10,10,10,110}, clientid => <<"c1">>, username => <<"u1">>}, - allow = emqx_access_control:check_acl(User3, subscribe, <<"t1">>), - allow = emqx_access_control:check_acl(User3, subscribe, <<"t1">>), - deny = emqx_access_control:check_acl(User3, subscribe, <<"t2">>),%% nomatch -> ignore -> emqx acl - deny = emqx_access_control:check_acl(User4, subscribe, <<"t1">>),%% nomatch -> ignore -> emqx acl - User5 = #{zone => external, peerhost => {127,0,0,1}, clientid => <<"c3">>, username => <<"u3">>}, - allow = emqx_access_control:check_acl(User5, subscribe, <<"t1">>), - allow = emqx_access_control:check_acl(User5, publish, <<"t1">>). - -t_acl_super(_) -> - reload([{password_hash, plain}, {auth_query, "select password from mqtt_user where username = '%u' limit 1"}]), - {ok, C} = emqtt:start_link([{host, "localhost"}, {clientid, <<"simpleClient">>}, - {username, <<"plain">>}, {password, <<"plain">>}]), - {ok, _} = emqtt:connect(C), - timer:sleep(10), - emqtt:subscribe(C, <<"TopicA">>, qos2), - emqtt:publish(C, <<"TopicA">>, <<"Payload">>, qos2), - timer:sleep(1000), - receive - {publish, #{payload := Payload}} -> - ?assertEqual(<<"Payload">>, Payload) - after - 1000 -> - ct:fail({receive_timeout, <<"Payload">>}), - ok - end, - emqtt:disconnect(C). - -reload(Config) when is_list(Config) -> - application:stop(?APP), - [application:set_env(?APP, K, V) || {K, V} <- Config], - application:start(?APP). - -init_acl() -> - {ok, Pid} = ecpool_worker:client(gproc_pool:pick_worker({ecpool, ?POOL})), - {ok, [], []} = epgsql:squery(Pid, ?DROP_ACL_TABLE), - {ok, [], []} = epgsql:squery(Pid, ?CREATE_ACL_TABLE), - {ok, _} = epgsql:equery(Pid, ?INIT_ACL). - -drop_acl() -> - {ok, Pid} = ecpool_worker:client(gproc_pool:pick_worker({ecpool, ?POOL})), - {ok, [], []}= epgsql:squery(Pid, ?DROP_ACL_TABLE). - -init_auth() -> - {ok, Pid} = ecpool_worker:client(gproc_pool:pick_worker({ecpool, ?POOL})), - {ok, [], []} = epgsql:squery(Pid, ?DROP_AUTH_TABLE), - {ok, [], []} = epgsql:squery(Pid, ?CREATE_AUTH_TABLE), - {ok, _} = epgsql:equery(Pid, ?INIT_AUTH). - -drop_auth() -> - {ok, Pid} = ecpool_worker:client(gproc_pool:pick_worker({ecpool, ?POOL})), - {ok, [], []} = epgsql:squery(Pid, ?DROP_AUTH_TABLE). diff --git a/apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data/ca-key.pem b/apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data/ca-key.pem deleted file mode 100644 index e9717011e..000000000 --- a/apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data/ca-key.pem +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEpAIBAAKCAQEA0kGUBi9NDp65jgdxKfizIfuSr2wpwb44yM9SuP4oUQSULOA2 -4iFpLR/c5FAYHU81y9Vx91dQjdZfffaBZuv2zVvteXUkol8Nez7boKbo2E41MTew -8edtNKZAQVvnaHAC2NCZxjchCzUCDEoUUcl+cIERZ8R48FBqK5iTVcMRIx1akwus -+dhBqP0ykA5TGOWZkJrLM9aUXSPQha9+wXlOpkvu0Ur2nkX8PPJnifWao9UShSar -ll1IqPZNCSlZMwcFYcQNBCpdvITUUYlHvMRQV64bUpOxUGDuJkQL3dLKBlNuBRlJ -BcjBAKw7rFnwwHZcMmQ9tan/dZzpzwjo/T0XjwIDAQABAoIBAQCSHvUqnzDkWjcG -l/Fzg92qXlYBCCC0/ugj1sHcwvVt6Mq5rVE3MpUPwTcYjPlVVTlD4aEEjm/zQuq2 -ddxUlOS+r4aIhHrjRT/vSS4FpjnoKeIZxGR6maVxk6DQS3i1QjMYT1CvSpzyVvKH -a+xXMrtmoKxh+085ZAmFJtIuJhUA2yEa4zggCxWnvz8ecLClUPfVDPhdLBHc3KmL -CRpHEC6L/wanvDPRdkkzfKyaJuIJlTDaCg63AY5sDkTW2I57iI/nJ3haSeidfQKz -39EfbnM1A/YprIakafjAu3frBIsjBVcxwGihZmL/YriTHjOggJF841kT5zFkkv2L -/530Wk6xAoGBAOqZLZ4DIi/zLndEOz1mRbUfjc7GQUdYplBnBwJ22VdS0P4TOXnd -UbJth2MA92NM7ocTYVFl4TVIZY/Y+Prxk7KQdHWzR7JPpKfx9OEVgtSqV0vF9eGI -rKp79Y1T4Mvc3UcQCXX6TP7nHLihEzpS8odm2LW4txrOiLsn4Fq/IWrLAoGBAOVv -6U4tm3lImotUupKLZPKEBYwruo9qRysoug9FiorP4TjaBVOfltiiHbAQD6aGfVtN -SZpZZtrs17wL7Xl4db5asgMcZd+8Hkfo5siR7AuGW9FZloOjDcXb5wCh9EvjJ74J -Cjw7RqyVymq9t7IP6wnVwj5Ck48YhlOZCz/mzlnNAoGAWq7NYFgLvgc9feLFF23S -IjpJQZWHJEITP98jaYNxbfzYRm49+GphqxwFinKULjFNvq7yHlnIXSVYBOu1CqOZ -GRwXuGuNmlKI7lZr9xmukfAqgGLMMdr4C4qRF4lFyufcLRz42z7exmWlx4ST/yaT -E13hBRWayeTuG5JFei6Jh1MCgYEAqmX4LyC+JFBgvvQZcLboLRkSCa18bADxhENG -FAuAvmFvksqRRC71WETmqZj0Fqgxt7pp3KFjO1rFSprNLvbg85PmO1s+6fCLyLpX -lESTu2d5D71qhK93jigooxalGitFm+SY3mzjq0/AOpBWOn+J/w7rqVPGxXLgaHv0 -l+vx+00CgYBOvo9/ImjwYii2jFl+sHEoCzlvpITi2temRlT2j6ulSjCLJgjwEFw9 -8e+vvfQumQOsutakUVyURrkMGNDiNlIv8kv5YLCCkrwN22E6Ghyi69MJUvHQXkc/ -QZhjn/luyfpB5f/BeHFS2bkkxAXo+cfG45ApY3Qfz6/7o+H+vDa6/A== ------END RSA PRIVATE KEY----- diff --git a/apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data/ca.pem b/apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data/ca.pem deleted file mode 100644 index 00b31d8a4..000000000 --- a/apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data/ca.pem +++ /dev/null @@ -1,19 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDAzCCAeugAwIBAgIBATANBgkqhkiG9w0BAQsFADA8MTowOAYDVQQDDDFNeVNR -TF9TZXJ2ZXJfOC4wLjE5X0F1dG9fR2VuZXJhdGVkX0NBX0NlcnRpZmljYXRlMB4X -DTIwMDYxMTAzMzg0NloXDTMwMDYwOTAzMzg0NlowPDE6MDgGA1UEAwwxTXlTUUxf -U2VydmVyXzguMC4xOV9BdXRvX0dlbmVyYXRlZF9DQV9DZXJ0aWZpY2F0ZTCCASIw -DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANJBlAYvTQ6euY4HcSn4syH7kq9s -KcG+OMjPUrj+KFEElCzgNuIhaS0f3ORQGB1PNcvVcfdXUI3WX332gWbr9s1b7Xl1 -JKJfDXs+26Cm6NhONTE3sPHnbTSmQEFb52hwAtjQmcY3IQs1AgxKFFHJfnCBEWfE -ePBQaiuYk1XDESMdWpMLrPnYQaj9MpAOUxjlmZCayzPWlF0j0IWvfsF5TqZL7tFK -9p5F/DzyZ4n1mqPVEoUmq5ZdSKj2TQkpWTMHBWHEDQQqXbyE1FGJR7zEUFeuG1KT -sVBg7iZEC93SygZTbgUZSQXIwQCsO6xZ8MB2XDJkPbWp/3Wc6c8I6P09F48CAwEA -AaMQMA4wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEADKz6bIpP5anp -GgLB0jkclRWuMlS4qqIt4itSsMXPJ/ezpHwECixmgW2TIQl6S1woRkUeMxhT2/Ay -Sn/7aKxuzRagyE5NEGOvrOuAP5RO2ZdNJ/X3/Rh533fK1sOTEEbSsWUvW6iSkZef -rsfZBVP32xBhRWkKRdLeLB4W99ADMa0IrTmZPCXHSSE2V4e1o6zWLXcOZeH1Qh8N -SkelBweR+8r1Fbvy1r3s7eH7DCbYoGEDVLQGOLvzHKBisQHmoDnnF5E9g1eeNRdg -o+vhOKfYCOzeNREJIqS42PHcGhdNRk90ycigPmfUJclz1mDHoMjKR2S5oosTpr65 -tNPx3CL7GA== ------END CERTIFICATE----- diff --git a/apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data/client-cert.pem b/apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data/client-cert.pem deleted file mode 100644 index aad1404ca..000000000 --- a/apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data/client-cert.pem +++ /dev/null @@ -1,19 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDBDCCAeygAwIBAgIBAzANBgkqhkiG9w0BAQsFADA8MTowOAYDVQQDDDFNeVNR -TF9TZXJ2ZXJfOC4wLjE5X0F1dG9fR2VuZXJhdGVkX0NBX0NlcnRpZmljYXRlMB4X -DTIwMDYxMTAzMzg0N1oXDTMwMDYwOTAzMzg0N1owQDE+MDwGA1UEAww1TXlTUUxf -U2VydmVyXzguMC4xOV9BdXRvX0dlbmVyYXRlZF9DbGllbnRfQ2VydGlmaWNhdGUw -ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDVYSWpOvCTupz82fc85Opv -EQ7rkB8X2oOMyBCpkyHKBIr1ZQgRDWBp9UVOASq3GnSElm6+T3Kb1QbOffa8GIlw -sjAueKdq5L2eSkmPIEQ7eoO5kEW+4V866hE1LeL/PmHg2lGP0iqZiJYtElhHNQO8 -3y9I7cm3xWMAA3SSWikVtpJRn3qIp2QSrH+tK+/HHbE5QwtPxdir4ULSCSOaM5Yh -Wi5Oto88TZqe1v7SXC864JVvO4LuS7TuSreCdWZyPXTJFBFeCEWSAxonKZrqHbBe -CwKML6/0NuzjaQ51c2tzmVI6xpHj3nnu4cSRx6Jf9WBm+35vm0wk4pohX3ptdzeV -AgMBAAGjDTALMAkGA1UdEwQCMAAwDQYJKoZIhvcNAQELBQADggEBAByQ5zSNeFUH -Aw7JlpZHtHaSEeiiyBHke20ziQ07BK1yi/ms2HAWwQkpZv149sjNuIRH8pkTmkZn -g8PDzSefjLbC9AsWpWV0XNV22T/cdobqLqMBDDZ2+5bsV+jTrOigWd9/AHVZ93PP -IJN8HJn6rtvo2l1bh/CdsX14uVSdofXnuWGabNTydqtMvmCerZsdf6qKqLL+PYwm -RDpgWiRUY7KPBSSlKm/9lJzA+bOe4dHeJzxWFVCJcbpoiTFs1je1V8kKQaHtuW39 -ifX6LTKUMlwEECCbDKM8Yq2tm8NjkjCcnFDtKg8zKGPUu+jrFMN5otiC3wnKcP7r -O9EkaPcgYH8= ------END CERTIFICATE----- diff --git a/apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data/client-key.pem b/apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data/client-key.pem deleted file mode 100644 index 6789d0291..000000000 --- a/apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data/client-key.pem +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEowIBAAKCAQEA1WElqTrwk7qc/Nn3POTqbxEO65AfF9qDjMgQqZMhygSK9WUI -EQ1gafVFTgEqtxp0hJZuvk9ym9UGzn32vBiJcLIwLninauS9nkpJjyBEO3qDuZBF -vuFfOuoRNS3i/z5h4NpRj9IqmYiWLRJYRzUDvN8vSO3Jt8VjAAN0klopFbaSUZ96 -iKdkEqx/rSvvxx2xOUMLT8XYq+FC0gkjmjOWIVouTraPPE2antb+0lwvOuCVbzuC -7ku07kq3gnVmcj10yRQRXghFkgMaJyma6h2wXgsCjC+v9Dbs42kOdXNrc5lSOsaR -49557uHEkceiX/VgZvt+b5tMJOKaIV96bXc3lQIDAQABAoIBAF7yjXmSOn7h6P0y -WCuGiTLG2mbDiLJqj2LTm2Z5i+2Cu/qZ7E76Ls63TxF4v3MemH5vGfQhEhR5ZD/6 -GRJ1sKKvB3WGRqjwA9gtojHH39S/nWGy6vYW/vMOOH37XyjIr3EIdIaUtFQBTSHd -Kd71niYrAbVn6fyWHolhADwnVmTMOl5OOAhCdEF4GN3b5aIhIu8BJ7EUzTtHBJIj -CAEfjZFjDs1y1cIgGFJkuIQxMfCpq5recU2qwip7YO6fk//WEjOPu7kSf5IEswL8 -jg1dea9rGBV6KaD2xsgsC6Ll6Sb4BbsrHMfflG3K2Lk3RdVqqTFp1Fn1PTLQE/1S -S/SZPYECgYEA9qYcHKHd0+Q5Ty5wgpxKGa4UCWkpwvfvyv4bh8qlmxueB+l2AIdo -ZvkM8gTPagPQ3WypAyC2b9iQu70uOJo1NizTtKnpjDdN1YpDjISJuS/P0x73gZwy -gmoM5AzMtN4D6IbxXtXnPaYICvwLKU80ouEN5ZPM4/ODLUu6gsp0v2UCgYEA3Xgi -zMC4JF0vEKEaK0H6QstaoXUmw/lToZGH3TEojBIkb/2LrHUclygtONh9kJSFb89/ -jbmRRLAOrx3HZKCNGUmF4H9k5OQyAIv6OGBinvLGqcbqnyNlI+Le8zxySYwKMlEj -EMrBCLmSyi0CGFrbZ3mlj/oCET/ql9rNvcK+DHECgYAEx5dH3sMjtgp+RFId1dWB -xePRgt4yTwewkVgLO5wV82UOljGZNQaK6Eyd7AXw8f38LHzh+KJQbIvxd2sL4cEi -OaAoohpKg0/Y0YMZl//rPMf0OWdmdZZs/I0fZjgZUSwWN3c59T8z7KG/RL8an9RP -S7kvN7wCttdV61/D5RR6GQKBgDxCe/WKWpBKaovzydMLWLTj7/0Oi0W3iXHkzzr4 -LTgvl4qBSofaNbVLUUKuZTv5rXUG2IYPf99YqCYtzBstNDc1MiAriaBeFtzfOW4t -i6gEFtoLLbuvPc3N5Sv5vn8Ug5G9UfU3td5R4AbyyCcoUZqOFuZd+EIJSiOXfXOs -kVmBAoGBAIU9aPAqhU5LX902oq8KsrpdySONqv5mtoStvl3wo95WIqXNEsFY60wO -q02jKQmJJ2MqhkJm2EoF2Mq8+40EZ5sz8LdgeQ/M0yQ9lAhPi4rftwhpe55Ma9dk -SE9X1c/DMCBEaIjJqVXdy0/EeArwpb8sHkguVVAZUWxzD+phm1gs ------END RSA PRIVATE KEY----- diff --git a/apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data/private_key.pem b/apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data/private_key.pem deleted file mode 100644 index 8fbf6bdec..000000000 --- a/apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data/private_key.pem +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEpAIBAAKCAQEA1zVmMhPqpSPMmYkKh5wwlRD5XuS8YWJKEM6tjFx61VK8qxHE -YngkC2KnL5EuKAjQZIF3tJskwt0hAat047CCCZxrkNEpbVvSnvnk+A/8bg/Ww1n3 -qxzfifhsWfpUKlDnwrtH+ftt+5rZeEkf37XAPy7ZjzecAF9SDV6WSiPeAxUX2+hN -dId42Pf45woo4LFGUlQeagCFkD/R0dpNIMGwcnkKCUikiBqr2ijSIgvRtBfZ9fBG -jFGER2uE/Eay4AgcQsHue8skRwDCng8OnqtPnBtTytmqTy9V/BRgsVKUoksm6wsx -kUYwgHeaq7UCvlCm25SZ7yRyd4k8t0BKDf2h+wIDAQABAoIBAEQcrHmRACTADdNS -IjkFYALt2l8EOfMAbryfDSJtapr1kqz59JPNvmq0EIHnixo0n/APYdmReLML1ZR3 -tYkSpjVwgkLVUC1CcIjMQoGYXaZf8PLnGJHZk45RR8m6hsTV0mQ5bfBaeVa2jbma -OzJMjcnxg/3l9cPQZ2G/3AUfEPccMxOXp1KRz3mUQcGnKJGtDbN/kfmntcwYoxaE -Zg4RoeKAoMpK1SSHAiJKe7TnztINJ7uygR9XSzNd6auY8A3vomSIjpYO7XL+lh7L -izm4Ir3Gb/eCYBvWgQyQa2KCJgK/sQyEs3a09ngofSEUhQJQYhgZDwUj+fDDOGqj -hCZOA8ECgYEA+ZWuHdcUQ3ygYhLds2QcogUlIsx7C8n/Gk/FUrqqXJrTkuO0Eqqa -B47lCITvmn2zm0ODfSFIARgKEUEDLS/biZYv7SUTrFqBLcet+aGI7Dpv91CgB75R -tNzcIf8VxoiP0jPqdbh9mLbbxGi5Uc4p9TVXRljC4hkswaouebWee0sCgYEA3L2E -YB3kiHrhPI9LHS5Px9C1w+NOu5wP5snxrDGEgaFCvL6zgY6PflacppgnmTXl8D1x -im0IDKSw5dP3FFonSVXReq3CXDql7UnhfTCiLDahV7bLxTH42FofcBpDN3ERdOal -58RwQh6VrLkzQRVoObo+hbGlFiwwSAfQC509FhECgYBsRSBpVXo25IN2yBRg09cP -+gdoFyhxrsj5kw1YnB13WrrZh+oABv4WtUhp77E5ZbpaamlKCPwBbXpAjeFg4tfr -0bksuN7V79UGFQ9FsWuCfr8/nDwv38H2IbFlFhFONMOfPmJBey0Q6JJhm8R41mSh -OOiJXcv85UrjIH5U0hLUDQKBgQDVLOU5WcUJlPoOXSgiT0ZW5xWSzuOLRUUKEf6l -19BqzAzCcLy0orOrRAPW01xylt2v6/bJw1Ahva7k1ZZo/kOwjANYoZPxM+ZoSZBN -MXl8j2mzZuJVV1RFxItV3NcLJNPB/Lk+IbRz9kt/2f9InF7iWR3mSU/wIM6j0X+2 -p6yFsQKBgQCM/ldWb511lA+SNkqXB2P6WXAgAM/7+jwsNHX2ia2Ikufm4SUEKMSv -mti/nZkHDHsrHU4wb/2cOAywMELzv9EHzdcoenjBQP65OAc/1qWJs+LnBcCXfqKk -aHjEZW6+brkHdRGLLY3YAHlt/AUL+RsKPJfN72i/FSpmu+52G36eeQ== ------END RSA PRIVATE KEY----- diff --git a/apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data/public_key.pem b/apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data/public_key.pem deleted file mode 100644 index f9772b533..000000000 --- a/apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data/public_key.pem +++ /dev/null @@ -1,9 +0,0 @@ ------BEGIN PUBLIC KEY----- -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1zVmMhPqpSPMmYkKh5ww -lRD5XuS8YWJKEM6tjFx61VK8qxHEYngkC2KnL5EuKAjQZIF3tJskwt0hAat047CC -CZxrkNEpbVvSnvnk+A/8bg/Ww1n3qxzfifhsWfpUKlDnwrtH+ftt+5rZeEkf37XA -Py7ZjzecAF9SDV6WSiPeAxUX2+hNdId42Pf45woo4LFGUlQeagCFkD/R0dpNIMGw -cnkKCUikiBqr2ijSIgvRtBfZ9fBGjFGER2uE/Eay4AgcQsHue8skRwDCng8OnqtP -nBtTytmqTy9V/BRgsVKUoksm6wsxkUYwgHeaq7UCvlCm25SZ7yRyd4k8t0BKDf2h -+wIDAQAB ------END PUBLIC KEY----- diff --git a/apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data/server-cert.pem b/apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data/server-cert.pem deleted file mode 100644 index a2f9688df..000000000 --- a/apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data/server-cert.pem +++ /dev/null @@ -1,19 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDBDCCAeygAwIBAgIBAjANBgkqhkiG9w0BAQsFADA8MTowOAYDVQQDDDFNeVNR -TF9TZXJ2ZXJfOC4wLjE5X0F1dG9fR2VuZXJhdGVkX0NBX0NlcnRpZmljYXRlMB4X -DTIwMDYxMTAzMzg0NloXDTMwMDYwOTAzMzg0NlowQDE+MDwGA1UEAww1TXlTUUxf -U2VydmVyXzguMC4xOV9BdXRvX0dlbmVyYXRlZF9TZXJ2ZXJfQ2VydGlmaWNhdGUw -ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCcEnEm5hqP1EbEJycOz8Ua -NWp29QdpFUzTWhkKGhVXk+0msmNTw4NBAFB42moY44OU8wvDideOlJNhPRWveD8z -G2lxzJA91p0UK4et8ia9MmeuCGhdC9jxJ8X69WNlUiPyy0hI/ZsqRq9Z0C2eW0iL -JPXsy4X8Xpw3SFwoXf5pR9RFY5Pb2tuyxqmSestu2VXT/NQjJg4CVDR3mFcHPXZB -4elRzH0WshExEGkgy0bg20MJeRc2Qdb5Xx+EakbmwroDWaCn3NSGqQ7jv6Vw0doy -TGvS6h6RHBxnyqRfRgKGlCoOMG9/5+rFJC00QpCUG2vHXHWGoWlMlJ3foN7rj5v9 -AgMBAAGjDTALMAkGA1UdEwQCMAAwDQYJKoZIhvcNAQELBQADggEBAJ5zt2rj4Ag6 -zpN59AWC1Fur8g8l41ksHkSpKPp+PtyO/ngvbMqBpfmK1e7JCKZv/68QXfMyWWAI -hwalqZkXXWHKjuz3wE7dE25PXFXtGJtcZAaj10xt98fzdqt8lQSwh2kbfNwZIz1F -sgAStgE7+ZTcqTgvNB76Os1UK0to+/P0VBWktaVFdyub4Nc2SdPVnZNvrRBXBwOD -3V8ViwywDOFoE7DvCvwx/SVsvoC0Z4j3AMMovO6oHicP7uU83qsQgm1Qru3YeoLR -+DoVi7IPHbWvN7MqFYn3YjNlByO2geblY7MR0BlqbFlmFrqLsUfjsh2ys7/U/knC -dN/klu446fI= ------END CERTIFICATE----- diff --git a/apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data/server-key.pem b/apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data/server-key.pem deleted file mode 100644 index a1dfd5f78..000000000 --- a/apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data/server-key.pem +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEowIBAAKCAQEAnBJxJuYaj9RGxCcnDs/FGjVqdvUHaRVM01oZChoVV5PtJrJj -U8ODQQBQeNpqGOODlPMLw4nXjpSTYT0Vr3g/MxtpccyQPdadFCuHrfImvTJnrgho -XQvY8SfF+vVjZVIj8stISP2bKkavWdAtnltIiyT17MuF/F6cN0hcKF3+aUfURWOT -29rbssapknrLbtlV0/zUIyYOAlQ0d5hXBz12QeHpUcx9FrIRMRBpIMtG4NtDCXkX -NkHW+V8fhGpG5sK6A1mgp9zUhqkO47+lcNHaMkxr0uoekRwcZ8qkX0YChpQqDjBv -f+fqxSQtNEKQlBtrx1x1hqFpTJSd36De64+b/QIDAQABAoIBAFiah66Dt9SruLkn -WR8piUaFyLlcBib8Nq9OWSTJBhDAJERxxb4KIvvGB+l0ZgNXNp5bFPSfzsZdRwZP -PX5uj8Kd71Dxx3mz211WESMJdEC42u+MSmN4lGLkJ5t/sDwXU91E1vbJM0ve8THV -4/Ag9qA4DX2vVZOeyqT/6YHpSsPNZplqzrbAiwrfHwkctHfgqwOf3QLfhmVQgfCS -VwidBldEUv2whSIiIxh4Rv5St4kA68IBCbJxdpOpyuQBkk6CkxZ7VN9FqOuSd4Pk -Wm7iWyBMZsCmELZh5XAXld4BEt87C5R4CvbPBDZxAv3THk1DNNvpy3PFQfwARRFb -SAToYMECgYEAyL7U8yxpzHDYWd3oCx6vTi9p9N/z0FfAkWrRF6dm4UcSklNiT1Aq -EOnTA+SaW8tV3E64gCWcY23gNP8so/ZseWj6L+peHwtchaP9+KB7yGw2A+05+lOx -VetLTjAOmfpiUXFe5w1q4C1RGhLjZjjzW+GvwdAuchQgUEFaomrV+PUCgYEAxwfH -cmVGFbAktcjU4HSRjKSfawCrut+3YUOLybyku3Q/hP9amG8qkVTFe95CTLjLe2D0 -ccaTTpofFEJ32COeck0g0Ujn/qQ+KXRoauOYs4FB1DtqMpqB78wufWEUpDpbd9/h -J+gJdC/IADd4tJW9zA92g8IA7ZtFmqDtiSpQ0ekCgYAQGkaorvJZpN+l7cf0RGTZ -h7IfI2vCVZer0n6tQA9fmLzjoe6r4AlPzAHSOR8sp9XeUy43kUzHKQQoHCPvjw/K -eWJAP7OHF/k2+x2fOPhU7mEy1W+mJdp+wt4Kio5RSaVjVQ3AyPG+w8PSrJszEvRq -dWMMz+851WV2KpfjmWBKlQKBgQC++4j4DZQV5aMkSKV1CIZOBf3vaIJhXKEUFQPD -PmB4fBEjpwCg+zNGp6iktt65zi17o8qMjrb1mtCt2SY04eD932LZUHNFlwcLMmes -Ad+aiDLJ24WJL1f16eDGcOyktlblDZB5gZ/ovJzXEGOkLXglosTfo77OQculmDy2 -/UL2WQKBgGeKasmGNfiYAcWio+KXgFkHXWtAXB9B91B1OFnCa40wx+qnl71MIWQH -PQ/CZFNWOfGiNEJIZjrHsfNJoeXkhq48oKcT0AVCDYyLV0VxDO4ejT95mGW6njNd -JpvmhwwAjOvuWVr0tn4iXlSK8irjlJHmwcRjLTJq97vE9fsA2MjI ------END RSA PRIVATE KEY----- diff --git a/apps/emqx_auth_redis/.gitignore b/apps/emqx_auth_redis/.gitignore deleted file mode 100644 index 71ecbe89a..000000000 --- a/apps/emqx_auth_redis/.gitignore +++ /dev/null @@ -1,28 +0,0 @@ -.rebar/ -.eunit/ -.erlang.mk/ -emqttd_auth_redis.d -deps/ -ct.coverdata -logs/ -test/ct.cover.spec -ebin/ -*.o -*.beam -*.plt -erl_crash.dump -data -emqx_auth_redis.d -cover/ -eunit.coverdata -_build/ -rebar.lock -erlang.mk -*.conf.rendered -.rebar3/ -*.swp -rebar.lock -/.idea/ -.DS_Store -/.ci/redis/nodes.*.conf -/.ci/redis/*.log \ No newline at end of file diff --git a/apps/emqx_auth_redis/README.md b/apps/emqx_auth_redis/README.md deleted file mode 100644 index 9aa851f88..000000000 --- a/apps/emqx_auth_redis/README.md +++ /dev/null @@ -1,171 +0,0 @@ -emqx_auth_redis -=============== - -EMQ X Redis Authentication/ACL Plugin - -Features ---------- - -- Full *Authentication*, *Superuser*, *ACL* support -- IPv4, IPv6 support -- Connection pool by [ecpool](https://github.com/emqx/ecpool) -- Support `single`, `sentinel`, `cluster` deployment structures of Redis -- Completely cover Redis 5, Redis 6 in our tests - - -Build Plugin ------------- - -``` -make && make tests -``` - -Configure Plugin ----------------- - -File: etc/emqx_auth_redis.conf - -``` -## Redis server address. -## -## Value: Port | IP:Port -## -## Redis Server: 6379, 127.0.0.1:6379, localhost:6379, Redis Sentinel: 127.0.0.1:26379 -auth.redis.server = 127.0.0.1:6379 - -## redis sentinel cluster name -## auth.redis.sentinel = mymaster - -## Redis pool size. -## -## Value: Number -auth.redis.pool = 8 - -## Redis database no. -## -## Value: Number -auth.redis.database = 0 - -## Redis password. -## -## Value: String -## auth.redis.password = - -## Authentication query command. -## -## Value: Redis cmd -## -## Variables: -## - %u: username -## - %c: clientid -## - %C: common name of client TLS cert -## - %d: subject of client TLS cert -## -## Examples: -## - HGET mqtt_user:%u password -## - HMGET mqtt_user:%u password -## - HMGET mqtt_user:%u password salt -auth.redis.auth_cmd = HMGET mqtt_user:%u password - -## Password hash. -## -## Value: plain | md5 | sha | sha256 | bcrypt -auth.redis.password_hash = plain - -## sha256 with salt prefix -## auth.redis.password_hash = salt,sha256 - -## sha256 with salt suffix -## auth.redis.password_hash = sha256,salt - -## bcrypt with salt prefix -## auth.redis.password_hash = salt,bcrypt - -## pbkdf2 with macfun iterations dklen -## macfun: md4, md5, ripemd160, sha, sha224, sha256, sha384, sha512 -## auth.redis.password_hash = pbkdf2,sha256,1000,20 - -## Superuser query command. -## -## Value: Redis cmd -## -## Variables: -## - %u: username -## - %c: clientid -## - %C: common name of client TLS cert -## - %d: subject of client TLS cert -auth.redis.super_cmd = HGET mqtt_user:%u is_superuser - -## ACL query command. -## -## Value: Redis cmd -## -## Variables: -## - %u: username -## - %c: clientid -auth.redis.acl_cmd = HGETALL mqtt_acl:%u -``` - -SuperUser ---------- - -``` -HSET mqtt_user: is_superuser 1 -``` - -User Hash with Password Salt ----------------------------- - -Set a 'user' hash with 'password' 'salt' field, for example: - -``` -HMSET mqtt_user: password "password" salt "salt" -``` - -User Set with Password ------------------------ - -Set a 'user' Set with 'password' field for example: - -``` -HSET mqtt_user: password "password" -``` - -ACL Rule Hash -------------- - -The plugin uses a redis hash to store ACL rules: - -``` -HSET mqtt_acl: topic1 1 -HSET mqtt_acl: topic2 2 -HSET mqtt_acl: topic3 3 -``` - -NOTE: 1: subscribe, 2: publish, 3: pubsub - -Subscription Hash ------------------ - -NOTICE: Move to emqx_backend_redis... - -The plugin could store the static subscriptions into a redis Hash: - -``` -HSET mqtt_sub: topic1 0 -HSET mqtt_sub: topic2 1 -HSET mqtt_sub: topic3 2 -``` - -Load Plugin ------------ - -``` -./bin/emqx_ctl plugins load emqx_auth_redis -``` - -Author ------- - -EMQ X Team. - diff --git a/apps/emqx_auth_redis/etc/emqx_auth_redis.conf b/apps/emqx_auth_redis/etc/emqx_auth_redis.conf deleted file mode 100644 index 62a6e4fe1..000000000 --- a/apps/emqx_auth_redis/etc/emqx_auth_redis.conf +++ /dev/null @@ -1,131 +0,0 @@ -##-------------------------------------------------------------------- -## Redis Auth/ACL Plugin -##-------------------------------------------------------------------- -## Redis Server cluster type -## single Single redis server -## sentinel Redis cluster through sentinel -## cluster Redis through cluster -auth.redis.type = single - -## Redis server address. -## -## Value: Port | IP:Port -## -## Single Redis Server: 127.0.0.1:6379, localhost:6379 -## Redis Sentinel: "127.0.0.1:26379,127.0.0.2:26379,127.0.0.3:26379" -## Redis Cluster: "127.0.0.1:6379,127.0.0.2:6379,127.0.0.3:6379" -auth.redis.server = "127.0.0.1:6379" - -## Redis sentinel cluster name. -## -## Value: String -## auth.redis.sentinel = mymaster - -## Redis pool size. -## -## Value: Number -auth.redis.pool = 8 - -## Redis database no. -## -## Value: Number -auth.redis.database = 0 - -## Redis password. -## -## Value: String -## auth.redis.password = - -## Redis query timeout -## -## Value: Duration -## auth.redis.query_timeout = 5s - -## Authentication query command. -## -## Value: Redis cmd -## -## Variables: -## - %u: username -## - %c: clientid -## - %C: common name of client TLS cert -## - %d: subject of client TLS cert -## -## Examples: -## - "HGET mqtt_user:%u password" -## - "HMGET mqtt_user:%u password" -## - "HMGET mqtt_user:%u password salt" -auth.redis.auth_cmd = "HMGET mqtt_user:%u password" - -## Password hash. -## -## Value: plain | md5 | sha | sha256 | bcrypt -auth.redis.password_hash = plain - -## sha256 with salt prefix -## auth.redis.password_hash = "salt,sha256" - -## sha256 with salt suffix -## auth.redis.password_hash = "sha256,salt" - -## bcrypt with salt prefix -## auth.redis.password_hash = "salt,bcrypt" - -## pbkdf2 with macfun iterations dklen -## macfun: md4, md5, ripemd160, sha, sha224, sha256, sha384, sha512 -## auth.redis.password_hash = "pbkdf2,sha256,1000,20" - -## Superuser query command. -## -## Value: Redis cmd -## -## Variables: -## - %u: username -## - %c: clientid -## - %C: common name of client TLS cert -## - %d: subject of client TLS cert -auth.redis.super_cmd = "HGET mqtt_user:%u is_superuser" - -## ACL query command. -## -## Value: Redis cmd -## -## Variables: -## - %u: username -## - %c: clientid -auth.redis.acl_cmd = "HGETALL mqtt_acl:%u" - -## Redis ssl configuration. -## -## Value: on | off -# auth.redis.ssl.enable = off - -## CA certificate. -## -## Value: File -#auth.redis.ssl.cacertfile = path/to/your/cafile.pem - -## Client ssl certificate. -## -## Value: File -# auth.redis.ssl.certfile = path/to/your/certfile - -## Client ssl keyfile. -## -## Value: File -# auth.redis.ssl.keyfile = path/to/your/keyfile - -## In mode verify_none the default behavior is to allow all x509-path -## validation errors. -## -## Value: true | false -#auth.redis.ssl.verify = false - -## If not specified, the server's names returned in server's certificate is validated against -## what's provided `auth.redis.server` config's host part. -## Setting to 'disable' will make EMQ X ignore unmatched server names. -## If set with a host name, the server's names returned in server's certificate is validated -## against this value. -## -## Value: String | disable -## auth.redis.ssl.server_name_indication = disable \ No newline at end of file diff --git a/apps/emqx_auth_redis/include/emqx_auth_redis.hrl b/apps/emqx_auth_redis/include/emqx_auth_redis.hrl deleted file mode 100644 index 204d8ef70..000000000 --- a/apps/emqx_auth_redis/include/emqx_auth_redis.hrl +++ /dev/null @@ -1,23 +0,0 @@ - --define(APP, emqx_auth_redis). - --record(auth_metrics, { - success = 'client.auth.success', - failure = 'client.auth.failure', - ignore = 'client.auth.ignore' - }). - --record(acl_metrics, { - allow = 'client.acl.allow', - deny = 'client.acl.deny', - ignore = 'client.acl.ignore' - }). - --define(METRICS(Type), tl(tuple_to_list(#Type{}))). --define(METRICS(Type, K), #Type{}#Type.K). - --define(AUTH_METRICS, ?METRICS(auth_metrics)). --define(AUTH_METRICS(K), ?METRICS(auth_metrics, K)). - --define(ACL_METRICS, ?METRICS(acl_metrics)). --define(ACL_METRICS(K), ?METRICS(acl_metrics, K)). diff --git a/apps/emqx_auth_redis/priv/emqx_auth_redis.schema b/apps/emqx_auth_redis/priv/emqx_auth_redis.schema deleted file mode 100644 index db758d8c0..000000000 --- a/apps/emqx_auth_redis/priv/emqx_auth_redis.schema +++ /dev/null @@ -1,184 +0,0 @@ -%%-*- mode: erlang -*- -%% emqx_auth_redis config mapping - -{mapping, "auth.redis.type", "emqx_auth_redis.server", [ - {default, single}, - {datatype, {enum, [single, sentinel, cluster]}} -]}. - -{mapping, "auth.redis.server", "emqx_auth_redis.server", [ - {default, "127.0.0.1:6379"}, - {datatype, [string]} -]}. - -{mapping, "auth.redis.sentinel", "emqx_auth_redis.server", [ - {default, ""}, - {datatype, string}, - hidden -]}. - -{mapping, "auth.redis.pool", "emqx_auth_redis.server", [ - {default, 8}, - {datatype, integer} -]}. - -{mapping, "auth.redis.database", "emqx_auth_redis.server", [ - {default, 0}, - {datatype, integer} -]}. - -{mapping, "auth.redis.password", "emqx_auth_redis.server", [ - {default, ""}, - {datatype, string}, - hidden -]}. - -{mapping, "auth.redis.ssl.enable", "emqx_auth_redis.options", [ - {default, off}, - {datatype, flag} -]}. - -{mapping, "auth.redis.ssl.cacertfile", "emqx_auth_redis.options", [ - {datatype, string} -]}. - -{mapping, "auth.redis.ssl.certfile", "emqx_auth_redis.options", [ - {datatype, string} -]}. - -{mapping, "auth.redis.ssl.keyfile", "emqx_auth_redis.options", [ - {datatype, string} -]}. - -{mapping, "auth.redis.ssl.verify", "emqx_auth_redis.options", [ - {default, false}, - {datatype, {enum, [true, false]}} -]}. - -{mapping, "auth.redis.ssl.server_name_indication", "emqx_auth_redis.options", [ - {datatype, string} -]}. - -%% FIXME: compatible with 4.0-4.2 version format, plan to delete in 5.0 -{mapping, "auth.redis.cafile", "emqx_auth_redis.options", [ - {datatype, string} -]}. - -%% FIXME: compatible with 4.0-4.2 version format, plan to delete in 5.0 -{mapping, "auth.redis.certfile", "emqx_auth_redis.options", [ - {datatype, string} -]}. - -%% FIXME: compatible with 4.0-4.2 version format, plan to delete in 5.0 -{mapping, "auth.redis.keyfile", "emqx_auth_redis.options", [ - {datatype, string} -]}. - -{translation, "emqx_auth_redis.options", fun(Conf) -> - Ssl = cuttlefish:conf_get("auth.redis.ssl.enable", Conf, false), - Filter = fun(Opts) -> [{K, V} || {K, V} <- Opts, V =/= undefined] end, - case Ssl of - true -> - %% FIXME: compatible with 4.0-4.2 version format, plan to delete in 5.0 - CA = cuttlefish:conf_get( - "auth.redis.ssl.cacertfile", Conf, - cuttlefish:conf_get("auth.redis.cafile", Conf, undefined) - ), - Cert = cuttlefish:conf_get( - "auth.redis.ssl.certfile", Conf, - cuttlefish:conf_get("auth.redis.certfile", Conf, undefined) - ), - Key = cuttlefish:conf_get( - "auth.redis.ssl.keyfile", Conf, - cuttlefish:conf_get("auth.redis.keyfile", Conf, undefined) - ), - Verify = case cuttlefish:conf_get("auth.redis.ssl.verify", Conf, false) of - true -> verify_peer; - false -> verify_none - end, - SNI = case cuttlefish:conf_get("auth.redis.ssl.server_name_indication", Conf, undefined) of - "disable" -> disable; - SNI0 -> SNI0 - end, - [{options, [{ssl_options, - Filter([{cacertfile, CA}, - {certfile, Cert}, - {keyfile, Key}, - {verify, Verify}, - {server_name_indication, SNI} - ]) - }]}]; - _ -> [{options, []}] - end -end}. - -{translation, "emqx_auth_redis.server", fun(Conf) -> - Fun = fun(S) -> - case string:split(S, ":", trailing) of - [Domain] -> {Domain, 6379}; - [Domain, Port] -> {Domain, list_to_integer(Port)} - end - end, - Servers = cuttlefish:conf_get("auth.redis.server", Conf), - Type = cuttlefish:conf_get("auth.redis.type", Conf), - Server = case Type of - single -> - {Host, Port} = Fun(Servers), - [{host, Host}, {port, Port}]; - _ -> - S = string:tokens(Servers, ","), - [{servers, [Fun(S1) || S1 <- S]}] - end, - Pool = cuttlefish:conf_get("auth.redis.pool", Conf), - Passwd = cuttlefish:conf_get("auth.redis.password", Conf), - DB = cuttlefish:conf_get("auth.redis.database", Conf), - Sentinel = cuttlefish:conf_get("auth.redis.sentinel", Conf), - [{type, Type}, - {pool_size, Pool}, - {auto_reconnect, 1}, - {database, DB}, - {password, Passwd}, - {sentinel, Sentinel}] ++ Server -end}. - -{mapping, "auth.redis.query_timeout", "emqx_auth_redis.query_timeout", [ - {default, ""}, - {datatype, string} -]}. - -{translation, "emqx_auth_redis.query_timeout", fun(Conf) -> - case cuttlefish:conf_get("auth.redis.query_timeout", Conf) of - "" -> infinity; - Duration -> - case cuttlefish_duration:parse(Duration, ms) of - {error, Reason} -> error(Reason); - Ms when is_integer(Ms) -> Ms - end - end -end}. - -{mapping, "auth.redis.auth_cmd", "emqx_auth_redis.auth_cmd", [ - {datatype, string} -]}. - -{mapping, "auth.redis.password_hash", "emqx_auth_redis.password_hash", [ - {datatype, string} -]}. - -{mapping, "auth.redis.super_cmd", "emqx_auth_redis.super_cmd", [ - {datatype, string} -]}. - -{mapping, "auth.redis.acl_cmd", "emqx_auth_redis.acl_cmd", [ - {datatype, string} -]}. - -{translation, "emqx_auth_redis.password_hash", fun(Conf) -> - HashValue = cuttlefish:conf_get("auth.redis.password_hash", Conf), - case string:tokens(HashValue, ",") of - [Hash] -> list_to_atom(Hash); - [Prefix, Suffix] -> {list_to_atom(Prefix), list_to_atom(Suffix)}; - [Hash, MacFun, Iterations, Dklen] -> {list_to_atom(Hash), list_to_atom(MacFun), list_to_integer(Iterations), list_to_integer(Dklen)}; - _ -> plain - end -end}. diff --git a/apps/emqx_auth_redis/rebar.config b/apps/emqx_auth_redis/rebar.config deleted file mode 100644 index 750f07809..000000000 --- a/apps/emqx_auth_redis/rebar.config +++ /dev/null @@ -1,24 +0,0 @@ -{deps, - %% NOTE: mind poolboy version when updating eredis_cluster version - %% poolboy version may clash with emqx_auth_mongo - [ - {poolboy, {git, "https://github.com/emqx/poolboy.git", {tag, "1.5.2"}}} - ]}. - -{erl_opts, [warn_unused_vars, - warn_shadow_vars, - warn_unused_import, - warn_obsolete_guard, - debug_info, - compressed - ]}. -{overrides, [{add, [{erl_opts, [compressed]}]}]}. - -{xref_checks, [undefined_function_calls, undefined_functions, - locals_not_used, deprecated_function_calls, - warnings_as_errors, deprecated_functions - ]}. - -{cover_enabled, true}. -{cover_opts, [verbose]}. -{cover_export_enabled, true}. diff --git a/apps/emqx_auth_redis/src/emqx_acl_redis.erl b/apps/emqx_auth_redis/src/emqx_acl_redis.erl deleted file mode 100644 index 47f5acbba..000000000 --- a/apps/emqx_auth_redis/src/emqx_acl_redis.erl +++ /dev/null @@ -1,86 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_acl_redis). - --include("emqx_auth_redis.hrl"). - --include_lib("emqx/include/emqx.hrl"). --include_lib("emqx/include/logger.hrl"). - --export([ register_metrics/0 - , check_acl/5 - , description/0 - ]). - --spec(register_metrics() -> ok). -register_metrics() -> - lists:foreach(fun emqx_metrics:ensure/1, ?ACL_METRICS). - -check_acl(ClientInfo, PubSub, Topic, AclResult, Config) -> - case do_check_acl(ClientInfo, PubSub, Topic, AclResult, Config) of - ok -> emqx_metrics:inc(?ACL_METRICS(ignore)), ok; - {stop, allow} -> emqx_metrics:inc(?ACL_METRICS(allow)), {stop, allow}; - {stop, deny} -> emqx_metrics:inc(?ACL_METRICS(deny)), {stop, deny} - end. - -do_check_acl(#{username := <<$$, _/binary>>}, _PubSub, _Topic, _AclResult, _Config) -> - ok; -do_check_acl(ClientInfo, PubSub, Topic, _AclResult, - #{acl_cmd := AclCmd, timeout := Timeout, type := Type, pool := Pool}) -> - case emqx_auth_redis_cli:q(Pool, Type, AclCmd, ClientInfo, Timeout) of - {ok, []} -> ok; - {ok, Rules} -> - case match(ClientInfo, PubSub, Topic, Rules) of - allow -> {stop, allow}; - nomatch -> {stop, deny} - end; - {error, Reason} -> - ?LOG(error, "[Redis] do_check_acl error: ~p", [Reason]), - ok - end. - -match(_ClientInfo, _PubSub, _Topic, []) -> - nomatch; -match(ClientInfo, PubSub, Topic, [Filter, Access | Rules]) -> - case {match_topic(Topic, feed_var(ClientInfo, Filter)), - match_access(PubSub, b2i(Access))} of - {true, true} -> allow; - {_, _} -> match(ClientInfo, PubSub, Topic, Rules) - end. - -match_topic(Topic, Filter) -> - emqx_topic:match(Topic, Filter). - -match_access(subscribe, Access) -> - (1 band Access) > 0; -match_access(publish, Access) -> - (2 band Access) > 0. - -feed_var(#{clientid := ClientId, username := Username}, Str) -> - lists:foldl(fun({Var, Val}, Acc) -> - feed_var(Acc, Var, Val) - end, Str, [{"%u", Username}, {"%c", ClientId}]). - -feed_var(Str, _Var, undefined) -> - Str; -feed_var(Str, Var, Val) -> - re:replace(Str, Var, Val, [global, {return, binary}]). - -b2i(Bin) -> list_to_integer(binary_to_list(Bin)). - -description() -> "Redis ACL Module". - diff --git a/apps/emqx_auth_redis/src/emqx_auth_redis.app.src b/apps/emqx_auth_redis/src/emqx_auth_redis.app.src deleted file mode 100644 index 419131566..000000000 --- a/apps/emqx_auth_redis/src/emqx_auth_redis.app.src +++ /dev/null @@ -1,14 +0,0 @@ -{application, emqx_auth_redis, - [{description, "EMQ X Authentication/ACL with Redis"}, - {vsn, "4.4.0"}, % strict semver, bump manually! - {modules, []}, - {registered, [emqx_auth_redis_sup]}, - {applications, [kernel,stdlib,eredis,eredis_cluster,ecpool]}, - {mod, {emqx_auth_redis_app, []}}, - {env, []}, - {licenses, ["Apache-2.0"]}, - {maintainers, ["EMQ X Team "]}, - {links, [{"Homepage", "https://emqx.io/"}, - {"Github", "https://github.com/emqx/emqx-auth-redis"} - ]} - ]}. diff --git a/apps/emqx_auth_redis/src/emqx_auth_redis.erl b/apps/emqx_auth_redis/src/emqx_auth_redis.erl deleted file mode 100644 index 318a8b23a..000000000 --- a/apps/emqx_auth_redis/src/emqx_auth_redis.erl +++ /dev/null @@ -1,85 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_auth_redis). - --include("emqx_auth_redis.hrl"). - --include_lib("emqx/include/emqx.hrl"). --include_lib("emqx/include/logger.hrl"). - --export([ register_metrics/0 - , check/3 - , description/0 - ]). - --spec(register_metrics() -> ok). -register_metrics() -> - lists:foreach(fun emqx_metrics:ensure/1, ?AUTH_METRICS). - -check(ClientInfo = #{password := Password}, AuthResult, - #{auth_cmd := AuthCmd, - super_cmd := SuperCmd, - hash_type := HashType, - timeout := Timeout, - type := Type, - pool := Pool}) -> - CheckPass = case emqx_auth_redis_cli:q(Pool, Type, AuthCmd, ClientInfo, Timeout) of - {ok, PassHash} when is_binary(PassHash) -> - check_pass({PassHash, Password}, HashType); - {ok, [undefined|_]} -> - {error, not_found}; - {ok, [PassHash]} -> - check_pass({PassHash, Password}, HashType); - {ok, [PassHash, Salt|_]} -> - check_pass({PassHash, Salt, Password}, HashType); - {error, Reason} -> - ?LOG(error, "[Redis] Command: ~p failed: ~p", [AuthCmd, Reason]), - {error, not_found} - end, - case CheckPass of - ok -> - ok = emqx_metrics:inc(?AUTH_METRICS(success)), - IsSuperuser = is_superuser(Pool, Type, SuperCmd, ClientInfo, Timeout), - {stop, AuthResult#{is_superuser => IsSuperuser, - anonymous => false, - auth_result => success}}; - {error, not_found} -> - ok = emqx_metrics:inc(?AUTH_METRICS(ignore)); - {error, ResultCode} -> - ok = emqx_metrics:inc(?AUTH_METRICS(failure)), - ?LOG(error, "[Redis] Auth from redis failed: ~p", [ResultCode]), - {stop, AuthResult#{auth_result => ResultCode, anonymous => false}} - end. - -description() -> "Authentication with Redis". - --spec(is_superuser(atom(), atom(), undefined|list(), emqx_types:client(), timeout()) -> boolean()). -is_superuser(_Pool, _Type, undefined, _ClientInfo, _Timeout) -> false; -is_superuser(Pool, Type, SuperCmd, ClientInfo, Timeout) -> - case emqx_auth_redis_cli:q(Pool, Type, SuperCmd, ClientInfo, Timeout) of - {ok, undefined} -> false; - {ok, <<"1">>} -> true; - {ok, _Other} -> false; - {error, _Error} -> false - end. - -check_pass(Password, HashType) -> - case emqx_passwd:check_pass(Password, HashType) of - ok -> ok; - {error, _Reason} -> {error, not_authorized} - end. - diff --git a/apps/emqx_auth_redis/src/emqx_auth_redis_app.erl b/apps/emqx_auth_redis/src/emqx_auth_redis_app.erl deleted file mode 100644 index 3f0b6ce26..000000000 --- a/apps/emqx_auth_redis/src/emqx_auth_redis_app.erl +++ /dev/null @@ -1,70 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_auth_redis_app). - --behaviour(application). - --emqx_plugin(auth). - --include("emqx_auth_redis.hrl"). - --export([ start/2 - , stop/1 - ]). - -start(_StartType, _StartArgs) -> - {ok, Sup} = emqx_auth_redis_sup:start_link(), - _ = if_cmd_enabled(auth_cmd, fun load_auth_hook/1), - _ = if_cmd_enabled(acl_cmd, fun load_acl_hook/1), - {ok, Sup}. - -stop(_State) -> - emqx:unhook('client.authenticate', {emqx_auth_redis, check}), - emqx:unhook('client.check_acl', {emqx_acl_redis, check_acl}), - %% Ensure stop cluster pool if the server type is cluster - eredis_cluster:stop_pool(?APP). - -load_auth_hook(AuthCmd) -> - SuperCmd = application:get_env(?APP, super_cmd, undefined), - {ok, HashType} = application:get_env(?APP, password_hash), - {ok, Timeout} = application:get_env(?APP, query_timeout), - Type = proplists:get_value(type, application:get_env(?APP, server, [])), - Config = #{auth_cmd => AuthCmd, - super_cmd => SuperCmd, - hash_type => HashType, - timeout => Timeout, - type => Type, - pool => ?APP}, - ok = emqx_auth_redis:register_metrics(), - emqx:hook('client.authenticate', {emqx_auth_redis, check, [Config]}). - -load_acl_hook(AclCmd) -> - {ok, Timeout} = application:get_env(?APP, query_timeout), - Type = proplists:get_value(type, application:get_env(?APP, server, [])), - Config = #{acl_cmd => AclCmd, - timeout => Timeout, - type => Type, - pool => ?APP}, - ok = emqx_acl_redis:register_metrics(), - emqx:hook('client.check_acl', {emqx_acl_redis, check_acl, [Config]}). - -if_cmd_enabled(Par, Fun) -> - case application:get_env(?APP, Par) of - {ok, Cmd} -> Fun(Cmd); - undefined -> ok - end. - diff --git a/apps/emqx_auth_redis/src/emqx_auth_redis_cli.erl b/apps/emqx_auth_redis/src/emqx_auth_redis_cli.erl deleted file mode 100644 index 52ac39a7b..000000000 --- a/apps/emqx_auth_redis/src/emqx_auth_redis_cli.erl +++ /dev/null @@ -1,89 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_auth_redis_cli). - --behaviour(ecpool_worker). - --include("emqx_auth_redis.hrl"). - --include_lib("emqx/include/emqx.hrl"). --include_lib("emqx/include/logger.hrl"). - --import(proplists, [get_value/2, get_value/3]). - --export([ connect/1 - , q/5 - ]). - -%%-------------------------------------------------------------------- -%% Redis Connect/Query -%%-------------------------------------------------------------------- - -connect(Opts) -> - Sentinel = get_value(sentinel, Opts), - Host = case Sentinel =:= "" of - true -> get_value(host, Opts); - false -> - _ = eredis_sentinel:start_link(get_value(servers, Opts), get_value(options, Opts, [])), - "sentinel:" ++ Sentinel - end, - case eredis:start_link(Host, - get_value(port, Opts, 6379), - get_value(database, Opts, 0), - get_value(password, Opts, ""), - 3000, - 5000, - get_value(options, Opts, [])) of - {ok, Pid} -> {ok, Pid}; - {error, Reason = {connection_error, _}} -> - ?LOG(error, "[Redis] Can't connect to Redis server: Connection refused."), - {error, Reason}; - {error, Reason = {authentication_error, _}} -> - ?LOG(error, "[Redis] Can't connect to Redis server: Authentication failed."), - {error, Reason}; - {error, Reason} -> - ?LOG(error, "[Redis] Can't connect to Redis server: ~p", [Reason]), - {error, Reason} - end. - -%% Redis Query. --spec(q(atom(), atom(), string(), emqx_types:credentials(), timeout()) - -> {ok, undefined | binary() | list()} | {error, atom() | binary()}). -q(Pool, Type, CmdStr, Credentials, Timeout) -> - Cmd = string:tokens(replvar(CmdStr, Credentials), " "), - case Type of - cluster -> eredis_cluster:q(Pool, Cmd); - _ -> ecpool:with_client(Pool, fun(C) -> eredis:q(C, Cmd, Timeout) end) - end. - -replvar(Cmd, Credentials = #{cn := CN}) -> - replvar(repl(Cmd, "%C", CN), maps:remove(cn, Credentials)); -replvar(Cmd, Credentials = #{dn := DN}) -> - replvar(repl(Cmd, "%d", DN), maps:remove(dn, Credentials)); -replvar(Cmd, Credentials = #{clientid := ClientId}) -> - replvar(repl(Cmd, "%c", ClientId), maps:remove(clientid, Credentials)); -replvar(Cmd, Credentials = #{username := Username}) -> - replvar(repl(Cmd, "%u", Username), maps:remove(username, Credentials)); -replvar(Cmd, _) -> - Cmd. - -repl(S, _Var, undefined) -> - S; -repl(S, Var, Val) -> - NVal = re:replace(Val, "&", "\\\\&", [global, {return, list}]), - re:replace(S, Var, NVal, [{return, list}]). - diff --git a/apps/emqx_auth_redis/src/emqx_auth_redis_sup.erl b/apps/emqx_auth_redis/src/emqx_auth_redis_sup.erl deleted file mode 100644 index ef81eef86..000000000 --- a/apps/emqx_auth_redis/src/emqx_auth_redis_sup.erl +++ /dev/null @@ -1,43 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_auth_redis_sup). - --behaviour(supervisor). - --include("emqx_auth_redis.hrl"). - --export([start_link/0]). - --export([init/1]). - -start_link() -> - supervisor:start_link({local, ?MODULE}, ?MODULE, []). - -init([]) -> - {ok, Server} = application:get_env(?APP, server), - {ok, {{one_for_one, 10, 100}, pool_spec(Server)}}. - -pool_spec(Server) -> - Options = application:get_env(?APP, options, []), - case proplists:get_value(type, Server) of - cluster -> - {ok, _} = eredis_cluster:start_pool(?APP, Server ++ Options), - []; - _ -> - [ecpool:pool_spec(?APP, ?APP, emqx_auth_redis_cli, Server ++ Options)] - end. - diff --git a/apps/emqx_auth_redis/test/emqx_auth_redis_SUITE.erl b/apps/emqx_auth_redis/test/emqx_auth_redis_SUITE.erl deleted file mode 100644 index 24d3b20bd..000000000 --- a/apps/emqx_auth_redis/test/emqx_auth_redis_SUITE.erl +++ /dev/null @@ -1,186 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_auth_redis_SUITE). - --compile(export_all). --compile(nowarn_export_all). - --include_lib("emqx/include/emqx.hrl"). - --include_lib("common_test/include/ct.hrl"). - --include_lib("eunit/include/eunit.hrl"). - --define(APP, emqx_auth_redis). - --define(POOL(App), ecpool_worker:client(gproc_pool:pick_worker({ecpool, App}))). - --define(INIT_ACL, [{"mqtt_acl:test1", "topic1", "2"}, - {"mqtt_acl:test2", "topic2", "1"}, - {"mqtt_acl:test3", "topic3", "3"}]). - --define(INIT_AUTH, [{"mqtt_user:plain", ["password", "plain", "salt", "salt", "is_superuser", "1"]}, - {"mqtt_user:special&symbol", ["password", "plain", "salt", "salt", "is_superuser", "0"]}, - {"mqtt_user:md5", ["password", "1bc29b36f623ba82aaf6724fd3b16718", "salt", "salt", "is_superuser", "0"]}, - {"mqtt_user:sha", ["password", "d8f4590320e1343a915b6394170650a8f35d6926", "salt", "salt", "is_superuser", "0"]}, - {"mqtt_user:sha256", ["password", "5d5b09f6dcb2d53a5fffc60c4ac0d55fabdf556069d6631545f42aa6e3500f2e", "salt", "salt", "is_superuser", "0"]}, - {"mqtt_user:pbkdf2_password", ["password", "cdedb5281bb2f801565a1122b2563515", "salt", "ATHENA.MIT.EDUraeburn", "is_superuser", "0"]}, - {"mqtt_user:bcrypt_foo", ["password", "$2a$12$sSS8Eg.ovVzaHzi1nUHYK.HbUIOdlQI0iS22Q5rd5z.JVVYH6sfm6", "salt", "$2a$12$sSS8Eg.ovVzaHzi1nUHYK.", "is_superuser", "0"]}, - {"mqtt_user:bcrypt", ["password", "$2y$16$rEVsDarhgHYB0TGnDFJzyu5f.T.Ha9iXMTk9J36NCMWWM7O16qyaK", "salt", "salt", "is_superuser", "0"]}]). - -%%-------------------------------------------------------------------- -%% Setups -%%-------------------------------------------------------------------- - -all() -> - emqx_ct:all(?MODULE). - -init_per_suite(Cfg) -> - emqx_ct_helpers:start_apps([emqx_auth_redis], fun set_special_configs/1), - init_redis_rows(), - Cfg. - -end_per_suite(_Cfg) -> - deinit_redis_rows(), - emqx_ct_helpers:stop_apps([emqx_auth_redis]). - -set_special_configs(emqx) -> - application:set_env(emqx, allow_anonymous, false), - application:set_env(emqx, acl_nomatch, deny), - application:set_env(emqx, enable_acl_cache, false); -set_special_configs(_App) -> - ok. - -init_redis_rows() -> - %% Users - [q(["HMSET", Key|FiledValue]) || {Key, FiledValue} <- ?INIT_AUTH], - %% ACLs - Result = [q(["HSET", Key, Filed, Value]) || {Key, Filed, Value} <- ?INIT_ACL], - ct:pal("redis init result: ~p~n", [Result]). - -deinit_redis_rows() -> - AuthKeys = [Key || {Key, _Filed, _Value} <- ?INIT_AUTH], - AclKeys = [Key || {Key, _Value} <- ?INIT_ACL], - q(["DEL" | AuthKeys]), - q(["DEL" | AclKeys]). - -%%-------------------------------------------------------------------- -%% Cases -%%-------------------------------------------------------------------- - -t_check_auth(_) -> - Plain = #{clientid => <<"client1">>, username => <<"plain">>, zone => external}, - SpecialSymbol = #{clientid => <<"special_symbol">>, username => <<"special&symbol">>, zone => external}, - Md5 = #{clientid => <<"md5">>, username => <<"md5">>, zone => external}, - Sha = #{clientid => <<"sha">>, username => <<"sha">>, zone => external}, - Sha256 = #{clientid => <<"sha256">>, username => <<"sha256">>, zone => external}, - Pbkdf2 = #{clientid => <<"pbkdf2_password">>, username => <<"pbkdf2_password">>, zone => external}, - BcryptFoo = #{clientid => <<"bcrypt_foo">>, username => <<"bcrypt_foo">>, zone => external}, - User1 = #{clientid => <<"bcrypt_foo">>, username => <<"user">>, zone => external}, - User3 = #{clientid => <<"client3">>, zone => external}, - Bcrypt = #{clientid => <<"bcrypt">>, username => <<"bcrypt">>, zone => external}, - {error, _} = emqx_access_control:authenticate(User3#{password => <<>>}), - reload([{password_hash, plain}]), - {ok, #{is_superuser := true}} = emqx_access_control:authenticate(Plain#{password => <<"plain">>}), - {ok, #{is_superuser := false}} = emqx_access_control:authenticate(SpecialSymbol#{password => <<"plain">>}), - reload([{password_hash, md5}]), - {ok, #{is_superuser := false}} = emqx_access_control:authenticate(Md5#{password => <<"md5">>}), - reload([{password_hash, sha}]), - {ok, #{is_superuser := false}} = emqx_access_control:authenticate(Sha#{password => <<"sha">>}), - reload([{password_hash, sha256}]), - {ok, #{is_superuser := false}} = emqx_access_control:authenticate(Sha256#{password => <<"sha256">>}), - reload([{password_hash, bcrypt}]), - {ok, #{is_superuser := false}} = emqx_access_control:authenticate(Bcrypt#{password => <<"password">>}), - %%pbkdf2 sha - reload([{password_hash, {pbkdf2, sha, 1, 16}}, {auth_cmd, "HMGET mqtt_user:%u password salt"}]), - {ok, #{is_superuser := false}} = emqx_access_control:authenticate(Pbkdf2#{password => <<"password">>}), - reload([{password_hash, {salt, bcrypt}}]), - {ok, #{is_superuser := false}} = emqx_access_control:authenticate(BcryptFoo#{password => <<"foo">>}), - {error,_} = emqx_access_control:authenticate(User1#{password => <<"foo">>}), - {error, _} = emqx_access_control:authenticate(Bcrypt#{password => <<"password">>}). - -t_check_auth_hget(_) -> - q(["HSET", "mqtt_user:hset", "password", "hset"]), - q(["HSET", "mqtt_user:hset", "is_superuser", "1"]), - reload([{password_hash, plain}, {auth_cmd, "HGET mqtt_user:%u password"}]), - Hset = #{clientid => <<"hset">>, username => <<"hset">>, zone => external}, - {ok, #{is_superuser := true}} = emqx_access_control:authenticate(Hset#{password => <<"hset">>}). - -t_check_acl(_) -> - User1 = #{zone => external, clientid => <<"client1">>, username => <<"test1">>}, - User2 = #{zone => external, clientid => <<"client2">>, username => <<"test2">>}, - User3 = #{zone => external, clientid => <<"client3">>, username => <<"test3">>}, - User4 = #{zone => external, clientid => <<"client4">>, username => <<"$$user4">>}, - deny = emqx_access_control:check_acl(User1, subscribe, <<"topic1">>), - allow = emqx_access_control:check_acl(User1, publish, <<"topic1">>), - - deny = emqx_access_control:check_acl(User2, publish, <<"topic2">>), - allow = emqx_access_control:check_acl(User2, subscribe, <<"topic2">>), - allow = emqx_access_control:check_acl(User3, publish, <<"topic3">>), - allow = emqx_access_control:check_acl(User3, subscribe, <<"topic3">>), - deny = emqx_access_control:check_acl(User4, publish, <<"a/b/c">>). - -t_acl_super(_) -> - reload([{password_hash, plain}]), - {ok, C} = emqtt:start_link([{host, "localhost"}, - {clientid, <<"simpleClient">>}, - {username, <<"plain">>}, - {password, <<"plain">>}]), - {ok, _} = emqtt:connect(C), - timer:sleep(10), - emqtt:subscribe(C, <<"TopicA">>, qos2), - timer:sleep(1000), - emqtt:publish(C, <<"TopicA">>, <<"Payload">>, qos2), - timer:sleep(1000), - receive - {publish, #{payload := Payload}} -> - ?assertEqual(<<"Payload">>, Payload) - after - 1000 -> - ct:fail({receive_timeout, <<"Payload">>}), - ok - end, - emqtt:disconnect(C). - -t_check_cluster_connection(_) -> - ?assertMatch({error, _Reason}, reload([{server, [{type,cluster}, - {pool_size,8}, - {auto_reconnect,1}, - {database,0}, - {password,[]}, - {sentinel,[]}, - {servers,[{"wrong",6379},{"wrong",6380},{"wrong",6381}]}]}])). - - -%%-------------------------------------------------------------------- -%% Internal funcs -%%-------------------------------------------------------------------- - -reload(Config) when is_list(Config) -> - application:stop(?APP), - [application:set_env(?APP, K, V) || {K, V} <- Config], - application:start(?APP). - -q(Cmd) -> - {ok, Server} = application:get_env(?APP, server), - case proplists:get_value(type, Server) of - cluster -> - eredis_cluster:q(emqx_auth_redis, Cmd); - _ -> - {ok, Connection} = ?POOL(?APP), - eredis:q(Connection, Cmd) - end. diff --git a/apps/emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/ca.crt b/apps/emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/ca.crt deleted file mode 100644 index b46bef4e5..000000000 --- a/apps/emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/ca.crt +++ /dev/null @@ -1,29 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIE5jCCAs4CCQCc1DzEYETfKTANBgkqhkiG9w0BAQsFADA1MRMwEQYDVQQKDApS -ZWRpcyBUZXN0MR4wHAYDVQQDDBVDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMjAx -MDI5MDEzNDE2WhcNMzAxMDI3MDEzNDE2WjA1MRMwEQYDVQQKDApSZWRpcyBUZXN0 -MR4wHAYDVQQDDBVDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEB -AQUAA4ICDwAwggIKAoICAQC/RxC/zQ6+ThI2l+LT5tpuvljE7CPca5erahTjv1Pq -mbmHYIVlige9jvZKR/AaaHuhNRT6C4PDpD98TgrhSLSgMMFImoFMSnmFEOVave3O -y1qV9vtoHLMB9hO+t7P98KRi1sCoMdPIE/o5uEGSd4YgWbk3NllAV6me108UniWU -yZMCSEKmV9OpfQ+YfHFolESV92ajdViDbtRBjfDNwD7qb8zgigxIJvBzEnWF4RZl -4+KIiyoJ55AQ3omdEi0QwiRRRONFtB6kRSqjGS8genGnycX1ZNPRB8JeG3ESuFj9 -1WQUD0EMBXFB5agHoZjvtFwxOkUkA4XbcnpKddHGKRt4BAbm+YcizJaT7mRytGWZ -UoTrDWz8/Cc0BlwAfPEk6ogU/sLSZpdxjxwprCNB89UOI+q7ng7CYiFnxY9HHZeg -GCJxYfvpKM/eOT9mSLUug8EGITd0j2cusflO4Q243clPyRbTSSr39Pcpy8rfKApF -HkUuGIpa/qgAbez+lPlIydzpbrTgrnHvL1P6fCYTnHkcgSn8glBIKv3vh4zQd6df -JvcLv3WEka9+lyoCvJ0QH+/ITqrToyWa8g9fR3ajTlyMANesKxQejo80zCwk/0ns -SFKRIJc6vfnUJ12Vdxpmm1LeoJZnCYODNUeeksL1ahHCBGq4M8UJ+ycUM6N4ndWE -6QIDAQABMA0GCSqGSIb3DQEBCwUAA4ICAQAg0BaIi7lzrNb7xC42c+GJVrBq8Qf8 -7CBzP8SXYGUavQIYNRrtH8UgTOwaju9vOn3zoY8L59N6e+Icyt+Oh1FENcQMCZ2l -SP79iaY9A/dRV56p6NqNd3VWH+EuRGbQVatLdhJf3l5+W1z3Dum1YXmIn26acawF -GZVqLalgvLqPzPHHWEqz9RnmcvTu3w9YVb4NgbmY4byCb6mB2avt0iWQrY/fZSMe -FvRXurr0jwyIXBncqnXu97sCeccNc+fo3qZC1xxH9iXOIzrRg0ud7VGMTKcNLTTc -GqnbjNT8BC96Qp2Bs8J+JGZa3mT/usKBq2TT/3q6oKevuc23u/a5s1rztnqZgIe5 -RzfevJ79xdva6DMSq/8Yyd3I8hrs3oZKJbAce6ux01RsrCcY2O7gi4dAMoEGumxW -CS9XLchNy7QxQ+J2AKBZXd6AZjvTvloDGz/yC5EbdK/MnLz8oApK5Z8U/huEilFa -AymVWQWpmlX2KxW0nkCperlb7lcbPS+ZuH0+Zd9HOvqr9cpYMrwpF54q4vnzUQkR -Hsxoapv/FBsVoxtcOqrcxwGpYWCsV0VBnv9+1fzzZ83aK7CHDIeGVuKPyjkhHzLy -v7Ljuqg400wH0WB9pyEdK+O3F+xO3zJgf4o0JptOKOFBVVSkZWTrqlDjjbcnXBmh -dwgj2xYeigqHJA== ------END CERTIFICATE----- diff --git a/apps/emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/ca.key b/apps/emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/ca.key deleted file mode 100644 index b615a8c1e..000000000 --- a/apps/emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/ca.key +++ /dev/null @@ -1,51 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIJKAIBAAKCAgEAv0cQv80Ovk4SNpfi0+babr5YxOwj3GuXq2oU479T6pm5h2CF -ZYoHvY72SkfwGmh7oTUU+guDw6Q/fE4K4Ui0oDDBSJqBTEp5hRDlWr3tzstalfb7 -aByzAfYTvrez/fCkYtbAqDHTyBP6ObhBkneGIFm5NzZZQFepntdPFJ4llMmTAkhC -plfTqX0PmHxxaJRElfdmo3VYg27UQY3wzcA+6m/M4IoMSCbwcxJ1heEWZePiiIsq -CeeQEN6JnRItEMIkUUTjRbQepEUqoxkvIHpxp8nF9WTT0QfCXhtxErhY/dVkFA9B -DAVxQeWoB6GY77RcMTpFJAOF23J6SnXRxikbeAQG5vmHIsyWk+5kcrRlmVKE6w1s -/PwnNAZcAHzxJOqIFP7C0maXcY8cKawjQfPVDiPqu54OwmIhZ8WPRx2XoBgicWH7 -6SjP3jk/Zki1LoPBBiE3dI9nLrH5TuENuN3JT8kW00kq9/T3KcvK3ygKRR5FLhiK -Wv6oAG3s/pT5SMnc6W604K5x7y9T+nwmE5x5HIEp/IJQSCr974eM0HenXyb3C791 -hJGvfpcqArydEB/vyE6q06MlmvIPX0d2o05cjADXrCsUHo6PNMwsJP9J7EhSkSCX -Or351CddlXcaZptS3qCWZwmDgzVHnpLC9WoRwgRquDPFCfsnFDOjeJ3VhOkCAwEA -AQKCAgBF1jSPUtcnNGoB9MKki40FEgpnG7CcMcxWkYy++oQxC59phhwuTo807pWN -2WYYvj0lRrQ59ypMrBNh1zyxtFH+is6HK6I5sJddtiWHVAEXl7ejOWHhSVkyRh4/ -a+MTvGDIlZAR2N9yFZkuqc+HIoyeEyREvFsp2tfbXtFIvdUK1e4Oz0NGaJqnLzoa -epUNkdTYzFN1Ksr+ceCdbq2U8bQG9HrhIIYLcewol3zBPMVoviNfpy/aHenDvvyP -lKtPixKneXdhY7osT/SZSACk4w/MKydTyVRs5WBZ67sFErmrM9YuXMNrGDGZ1bfb -0Wx9WGSwtI258G9XCB0OQqYsq6WTMaEei+z8l0iarZi1l2bz2F89J+IBYM8RqSsa -E30F8AtEG32QJUfK3F6k6N4uLx6JZduJgLyzsSh6q51ghAJ8kD1vUkEeXffCzynp -hzwRHUw5O1jNLEBdKYHpSyszlFX6qbzR1YXypzZs/aehZi5d89eBKN8X/Fnbi9a2 -Q0MqpZ5J/1hH7zadJFibNyuOCP4CNO3Hm18PjyEFRrCMbSF293kY9GoiOlQiwNAT -MqrsyLYgHPCXKXpG/R60lyHEfWKO9sOjyh+mSbv3QfNZS32Fweuo0R/vGYmkmtGn -wpn2IeSmX8ychdQrSemJjwzjUl/EUN0lGRAlHEt4ZDf52vHZ4QKCAQEA9k7b2vpU -g3S3GRCMzhl8GKZloNSbnR/ZHE8b9PVNahp0bQcZj+1yimF35p27VZR662ZBoKLy -/MLyPT+ZyynykwypcTVA5U9CABpSlyZMeezLnFlBXeHHMeoXcBZfFqHeXSsNYbhW -OStf4BGwKf7m/V0P/QL9mNsA/iq2uugC1gHoyp422YUIQQvKkBiFyMl34Zp2URsX -yIwb9aVyg2GogKDtbDPIwW89l3BCBiwalvjR6UotbXh3PQgYbsv62rTJ8AN+E+XH -eSQPnmPR6EXtX2nDuov996qlbja+JQE3SAls4EXLbrLyjSlObjcvkA59r+kZgNIY -g88hv7e9ublfqwKCAQEAxs3d9Zmjh9ByCT6jOUVnRMqw0+lVyVOs0kp7nOCb4HKM -CnupZuJQHQVQt7VhgD7FrALmYwkpt2e/WllN9bPFHRJcsw+SylOqyPil9G4DO5XZ -YPvk6PeQ/c0cbREKhsYNXqj5fWdq5pRd8rE72rK82mhdGQtAAN7NOEW5fo5tqHDK -D079SZmpcgd8Wz9luNpnZRpNhO3ccKV5yf0S1LZOZBbG9t875OVNhxlQY5wwIBXv -8ab13zcFKG21tWvLzz80vgkMIp0A9xh0XznIRnH3NnBZB80Yubg4sIaWvX7bqZ4X -EE9HGeiamw6c6Sm/Lvh/H659ri95l7C9TgAfAA7puwKCAQEAgJ/N0BzJ5ZwdwckS -vs4wL+81QzfDy9nF1zK4tsMjGjWWdxkuECs/lWQw6Q2VtqtDRYqw2uI9YiGrvrBn -7+CH/KKwGZ5ltVoebU9Rsf0eEs3FxnAV4qD1FOvaMX59SaReKulAo7dPz6sG9kxG -YqfqmITwxH+7TwePDSvhINnoITn+B1F38z+1f8JYlcc4lhIfuIChKNmtId2I/E7Z -7iIhjIp9cfPY8qrUzzCgSfjeKdjmRZ2m+3PdUNHZcIK1DWE700r/nARylqBuR5h5 -FYLu4tSokdJpXdyPZ27O/SQValkBslzAT57Da1QW0RegjuoCWMqxtsQAaVTRmvyo -50QW4QKCAQBLJFbn1MmFtRjVO7KwG/Z7fu01O7WsIg9pcLOmSRNB06nw8GrIM3Q6 -c97dgRY4RgGrEXGJL1ZwNyuRd73Kx8cSRPV6zMEb7mHYEnuPluFr7Si7ypnsIF7S -P2umIdHLvSIijFW4u5UhUCTubWUFNZfCKb4+kA0CBzSkN150Yls6Vl9ZR+7emdD9 -A61SQ/Ur2IlKIpX4T3uJrFILMbejZMDefel4OEpIKw+Rp9TFwaxDBGer/AJk+0Pc -0xLiXrsrO2WxCnRmxNcvjjO2Jn33em4JSo+sLi5RTDtJJaXmPAPE6bcn9/8U4OFH -CE/wpVHY7B4ImIhyhQk9d5Ul3U/aUsivAoIBAG8zk3CnFAnii1ENxhPtE8GCinvs -NdsluVtvUgMcA8gNzvqLHLQCoIy/b1wkqxPVsdTq1gZ7+FX9D9LzW9JxrWeEZqVV -jrUQIbls6HZei7i5x0tPwh1shOZiijgY24I6HDX9QRKZ7H7lLw0HfJI9YBI1Hl8E -naOtCuzFiaYEPfbGQACL8/UuoOZaD31JQda2EGYysGRxxJ2ZNPJIphCrwRb/nQBG -7WwCSCzu0peFNhZPVvkWHaFN73Uv/MmgFkp8RZzw9TEENB05wluCZB1TYJAOe65n -HnRWSDvWYR4lzMtq5WASFLC0WrFTiJKRCuKPljjoTptbXsJKyskW/t/+XPM= ------END RSA PRIVATE KEY----- diff --git a/apps/emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/ca.txt b/apps/emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/ca.txt deleted file mode 100644 index cf4e2aba5..000000000 --- a/apps/emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/ca.txt +++ /dev/null @@ -1 +0,0 @@ -BFFAA2A065DFA6FC diff --git a/apps/emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/redis.crt b/apps/emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/redis.crt deleted file mode 100644 index 5eefadf62..000000000 --- a/apps/emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/redis.crt +++ /dev/null @@ -1,23 +0,0 @@ ------BEGIN CERTIFICATE----- -MIID1zCCAb8CCQC/+qKgZd+m/DANBgkqhkiG9w0BAQsFADA1MRMwEQYDVQQKDApS -ZWRpcyBUZXN0MR4wHAYDVQQDDBVDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMjAx -MDI5MDEzNDE2WhcNMjExMDI5MDEzNDE2WjAmMRMwEQYDVQQKDApSZWRpcyBUZXN0 -MQ8wDQYDVQQDDAZTZXJ2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB -AQDSs3bQ9sYi2AhFuHU75Ryk1HHSgfzA6pQAJilmJdTy0s5vyiWe1HQJaWkMcS5V -GVzGMK+c+OBqtXtDDninL3betg1YPMjSCOjPMOTC1H9K7+effwf7Iwpnw9Zro8mb -TEmMslIYhhcDedzT9Owli4QAgbgTn4l1BYuKX9CLrrKFtnr21miKu3ydViy9q7T1 -pib3eigvAyk7X2fadHFArGEttsXrD6cetPPkSF/1OLWNlqzUKXzhSyrBXzO44Kks -fwR/EpTiES9g4dNOL2wvKS/YE1fNKhiCENrNxTXQo1l0yOdm2+MeyOeHFzRuS0b/ -+uGDFOPPi04KXeO6dQ5olBCPAgMBAAEwDQYJKoZIhvcNAQELBQADggIBADn0E2vG -iQWe8/I7VbBdPhPNupVNcLvew10eIHxY2g5vSruCSVRQTgk8itVMRmDQxbb7gdDW -jnCRbxykxbLjM9iCRljnOCsIcTi7qO7JRl8niV8dtEpPOs9lZxEdNXjIV1iZoWf3 -arBbPQSyQZvTQHG6qbFnyCdMMyyXGGvEPGQDaBiKH+Ko1qeAbCi0zupChYvxmtZ8 -hSTPlMFezDT9bKoNY0pkJSELfokEPU/Pn6Lz/NVbdzmCMjVa/xmF3s31g+DGhz95 -4AyOnCr6o0aydPVVV3pB/BCezNXPUxpp53BG0w/K2f2DnKYCvGvJbqDAaJ8bG/J1 -EFSOmwobdwVxJz3KNubmo1qJ6xOl/YT7yyqPRQRM1SY8nZW+YcoJSZjOe8wJVlob -d0bOwN1C3HQwomyMWes187bEQP6Y36HuEbR1fK8yIOzGsGDKRFAFwQwMgw2M91lr -EJIP5NRD3OZRuiYDiVfVhDZDaNahrAMZUcPCgeCAwc4YG6Gp2sDtdorOl4kIJYWE -BbBZ0Jplq9+g6ciu5ChjAW8iFl0Ae5U24MxPGXnrxiRF4WWxLeZMVLXLDvlPqReD -CHII5ifyvGEt5+RhqtZC/L+HimL+5wQgOlntqhUdLb6yWRz7YW37PFMnUXU3MXe9 -uY7m73ZLluXiLojcZxU2+cx89u5FOJxrYtrj ------END CERTIFICATE----- diff --git a/apps/emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/redis.dh b/apps/emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/redis.dh deleted file mode 100644 index f7dd0569d..000000000 --- a/apps/emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/redis.dh +++ /dev/null @@ -1,8 +0,0 @@ ------BEGIN DH PARAMETERS----- -MIIBCAKCAQEAo2dgOzTnLK7c8AjkiTXxdmo2MJsyzTlNXUDxLfl2hgwic6benyQ3 -9iL95wKjYg2YpMhzbwux50D+9XeVkRatf1pRi/N9H911f90MO6penzUx/dxfOepN -qoGK/T9xO8e6aFCYOoQjJaZzQYC0HixJVadZd7wRlHkZ3siNKUU5QK68KaN3JE3J -R3yZ9A7MU/TVdwZyVIyoWF2+WJMQW+qaezoqiuVKZXXzzoqbj14ZrtPRmO26vMV/ -bmMuHwPsk9dL7tKnTWEOrs6NVHIQW+RxJuRE9wGa0qqzHAzysEQ8q9QYPRvGo5y+ -XRWosl1bHG4+EmvXsCCs35bcbKToi3NFWwIBAg== ------END DH PARAMETERS----- diff --git a/apps/emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/redis.key b/apps/emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/redis.key deleted file mode 100644 index b76303f14..000000000 --- a/apps/emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/redis.key +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEpgIBAAKCAQEA0rN20PbGItgIRbh1O+UcpNRx0oH8wOqUACYpZiXU8tLOb8ol -ntR0CWlpDHEuVRlcxjCvnPjgarV7Qw54py923rYNWDzI0gjozzDkwtR/Su/nn38H -+yMKZ8PWa6PJm0xJjLJSGIYXA3nc0/TsJYuEAIG4E5+JdQWLil/Qi66yhbZ69tZo -irt8nVYsvau09aYm93ooLwMpO19n2nRxQKxhLbbF6w+nHrTz5Ehf9Ti1jZas1Cl8 -4UsqwV8zuOCpLH8EfxKU4hEvYOHTTi9sLykv2BNXzSoYghDazcU10KNZdMjnZtvj -Hsjnhxc0bktG//rhgxTjz4tOCl3junUOaJQQjwIDAQABAoIBAQCP7CJ27nm9B0/v -P+ZkeUWtmaf+IOhjZlieGXMh4SmqjDCSz8QO0BRK8YPeCdmaK27huhPa521ztm9y -CIqFuLg7vKM06KBMR+Wu0TkRlFE3ANR4cC8lbnQHGRB4CjMGL3/16UCGm+FQcIdV -CPHdW4VZS0JPtSQRmS4N4RD0uOocxqGcVbCRqnJoNp1zyXhookgHfZsC3b3cgzC7 -qvI9F1oY4Yg4b9Lw5sNi3JXWtFth8JFOPyImRcE0ngcGZK4iWjiufNKWVeTmSmVy -njMZfj8xKSpfqO3sOTbJMdrH1v5pMrAR/Ed748HheXuL15Ur9n88683hMMATZInn -YzIqNSrBAoGBAO94YBB1hN+jSKw+2FbAhuuM0gWHREmLQuaF2vjeVXL3r6YofFmf -+oJNgoOWXsv4KO2MgKDv4qrz7RohhhQpOFm5PpapSH/di7u6KsbJLYSxv/TEqQFE -NPyGywwNDIkn1wPlnX3LXp26puj2Gtn21Z0trUrpgsDM99BaTBbqTR2xAoGBAOE+ -tw0GHD/6CRPfoBIgVilS/sUJ5VJYTTKo/y6ozovCAq4bt5LkYmAOy6q8paHb58Oc -J890+LEPhelM/ZJDDz9oQFfq5LvuzgNfzDRyIhgDSpghtFrdDxQZP1X1lSdh+MFW -gx0k9h8VuIPksBsIgcmUtyCYitxLFep/0tAA/GI/AoGBAMxexEVntjWSScROgh1P -hBXlAZycO4g0ZK0OEboRLYXHos1AghePM6Ee+0LIAzE6IdvR7DjtYVoagQCrGZ19 -LE1Ojf7QjEIr1kQpdrZeHQ3BERyY9c9R4ZKeiw1G2ar4KEV4Ifeop6AfGrF4z6Oz -R80znVBwhxl6FAhp98QaxCORAoGBAInkc/nEKN09u/rvpzYRl83aol+MDFjZ+ACw -lvBApZnHnw5pp3uE13jI9gRDUv8A+iS1X2XQzULQJwHJgV7eMOJ3dxSbl4Y5zuMf -7YqZ6KdctHjoAVqzBD0gq7Z7DuG6R6hMxx27d/VVvcz43preHV6D7YxF9pSgXv1d -XXi7ccbPAoGBAIeLzCYd+JGufHwbq7oNvSyXJjGMjsAQuErUQ0xXwo7VAyOere2P -Dwk67wq6vsmn38EAs7IkXDgIoTD9z69DNtcjr/3fARYfmDSWyHscRwyUaJ15WQcZ -TCXAPf70Vf0KGBpRkgD+Qnq+lMZ3dr1uINGdalI4AWsXje0dPKpd+W8U ------END RSA PRIVATE KEY----- From 09e995a1b2c414704093559388b854f39fb86f9d Mon Sep 17 00:00:00 2001 From: zhanghongtong Date: Fri, 25 Jun 2021 10:01:51 +0800 Subject: [PATCH 028/174] build: delete needless auth plugins --- README-CN.md | 2 +- README-JP.md | 2 +- README-RU.md | 2 +- README.md | 2 +- apps/emqx_authentication/rebar.config | 5 ++-- apps/emqx_management/test/emqx_mgmt_SUITE.erl | 10 +++---- .../test/emqx_mgmt_api_SUITE.erl | 26 +++++++++---------- deploy/docker/README.md | 23 ++++++++-------- rebar.config.erl | 8 ------ 9 files changed, 36 insertions(+), 44 deletions(-) diff --git a/README-CN.md b/README-CN.md index 7d2888327..2a9dcebf6 100644 --- a/README-CN.md +++ b/README-CN.md @@ -101,7 +101,7 @@ make dialyzer ##### 要分析特定的应用程序,(用逗号分隔的应用程序列表) ``` -DIALYZER_ANALYSE_APP=emqx_lwm2m,emqx_auth_jwt,emqx_auth_ldap make dialyzer +DIALYZER_ANALYSE_APP=emqx_lwm2m,emqx_authz make dialyzer ``` ## 社区 diff --git a/README-JP.md b/README-JP.md index b7afe8195..580a76268 100644 --- a/README-JP.md +++ b/README-JP.md @@ -95,7 +95,7 @@ make dialyzer ##### 特定のアプリケーションのみ解析する(アプリケーション名をコンマ区切りで入力) ``` -DIALYZER_ANALYSE_APP=emqx_lwm2m,emqx_auth_jwt,emqx_auth_ldap make dialyzer +DIALYZER_ANALYSE_APP=emqx_lwm2m,emqx_authz make dialyzer ``` ## コミュニティ diff --git a/README-RU.md b/README-RU.md index cddaba4a5..5001f3fd3 100644 --- a/README-RU.md +++ b/README-RU.md @@ -104,7 +104,7 @@ make dialyzer ##### Статический анализ части приложений (список через запятую) ``` -DIALYZER_ANALYSE_APP=emqx_lwm2m,emqx_auth_jwt,emqx_auth_ldap make dialyzer +DIALYZER_ANALYSE_APP=emqx_lwm2m,emqx_authz make dialyzer ``` ## Сообщество diff --git a/README.md b/README.md index 8d8ed8731..f76d7ae0a 100644 --- a/README.md +++ b/README.md @@ -103,7 +103,7 @@ make dialyzer ##### To Analyse specific apps, (list of comma separated apps) ``` -DIALYZER_ANALYSE_APP=emqx_lwm2m,emqx_auth_jwt,emqx_auth_ldap make dialyzer +DIALYZER_ANALYSE_APP=emqx_lwm2m,emqx_authz make dialyzer ``` ## Community diff --git a/apps/emqx_authentication/rebar.config b/apps/emqx_authentication/rebar.config index 0a0af8c29..89bd1f54a 100644 --- a/apps/emqx_authentication/rebar.config +++ b/apps/emqx_authentication/rebar.config @@ -1,4 +1,5 @@ -{deps, []}. +{deps, [{jose, {git, "https://github.com/potatosalad/erlang-jose", {tag, "1.11.1"}}} + ]}. {edoc_opts, [{preprocess, true}]}. {erl_opts, [warn_unused_vars, @@ -15,4 +16,4 @@ {cover_enabled, true}. {cover_opts, [verbose]}. -{cover_export_enabled, true}. \ No newline at end of file +{cover_export_enabled, true}. diff --git a/apps/emqx_management/test/emqx_mgmt_SUITE.erl b/apps/emqx_management/test/emqx_mgmt_SUITE.erl index 6826e050b..84d5f2ebb 100644 --- a/apps/emqx_management/test/emqx_mgmt_SUITE.erl +++ b/apps/emqx_management/test/emqx_mgmt_SUITE.erl @@ -53,7 +53,7 @@ groups() -> ]}]. apps() -> - [emqx_management, emqx_auth_mnesia, emqx_modules]. + [emqx_management, emqx_retainer, emqx_modules]. init_per_suite(Config) -> application:set_env(ekka, strict_mode, true), @@ -317,12 +317,12 @@ t_plugins_cmd(_) -> meck:expect(emqx_plugins, reload, fun(_) -> ok end), ?assertEqual(emqx_mgmt_cli:plugins(["list"]), ok), ?assertEqual( - emqx_mgmt_cli:plugins(["unload", "emqx_auth_mnesia"]), - "Plugin emqx_auth_mnesia unloaded successfully.\n" + emqx_mgmt_cli:plugins(["unload", "emqx_retainer"]), + "Plugin emqx_retainer unloaded successfully.\n" ), ?assertEqual( - emqx_mgmt_cli:plugins(["load", "emqx_auth_mnesia"]), - "Plugin emqx_auth_mnesia loaded successfully.\n" + emqx_mgmt_cli:plugins(["load", "emqx_retainer"]), + "Plugin emqx_retainer loaded successfully.\n" ), ?assertEqual( emqx_mgmt_cli:plugins(["unload", "emqx_management"]), diff --git a/apps/emqx_management/test/emqx_mgmt_api_SUITE.erl b/apps/emqx_management/test/emqx_mgmt_api_SUITE.erl index d4d284e69..22d4f51ef 100644 --- a/apps/emqx_management/test/emqx_mgmt_api_SUITE.erl +++ b/apps/emqx_management/test/emqx_mgmt_api_SUITE.erl @@ -285,61 +285,61 @@ t_nodes(_) -> meck:unload(emqx_mgmt). t_plugins(_) -> - application:ensure_all_started(emqx_auth_mnesia), + application:ensure_all_started(emqx_retainer), {ok, Plugins1} = request_api(get, api_path(["plugins"]), auth_header_()), [Plugins11] = filter(get(<<"data">>, Plugins1), <<"node">>, atom_to_binary(node(), utf8)), - [Plugin1] = filter(maps:get(<<"plugins">>, Plugins11), <<"name">>, <<"emqx_auth_mnesia">>), - ?assertEqual(<<"emqx_auth_mnesia">>, maps:get(<<"name">>, Plugin1)), + [Plugin1] = filter(maps:get(<<"plugins">>, Plugins11), <<"name">>, <<"emqx_retainer">>), + ?assertEqual(<<"emqx_retainer">>, maps:get(<<"name">>, Plugin1)), ?assertEqual(true, maps:get(<<"active">>, Plugin1)), {ok, _} = request_api(put, api_path(["plugins", - atom_to_list(emqx_auth_mnesia), + atom_to_list(emqx_retainer), "unload"]), auth_header_()), {ok, Error1} = request_api(put, api_path(["plugins", - atom_to_list(emqx_auth_mnesia), + atom_to_list(emqx_retainer), "unload"]), auth_header_()), ?assertEqual(<<"not_started">>, get(<<"message">>, Error1)), {ok, Plugins2} = request_api(get, api_path(["nodes", atom_to_list(node()), "plugins"]), auth_header_()), - [Plugin2] = filter(get(<<"data">>, Plugins2), <<"name">>, <<"emqx_auth_mnesia">>), - ?assertEqual(<<"emqx_auth_mnesia">>, maps:get(<<"name">>, Plugin2)), + [Plugin2] = filter(get(<<"data">>, Plugins2), <<"name">>, <<"emqx_retainer">>), + ?assertEqual(<<"emqx_retainer">>, maps:get(<<"name">>, Plugin2)), ?assertEqual(false, maps:get(<<"active">>, Plugin2)), {ok, _} = request_api(put, api_path(["nodes", atom_to_list(node()), "plugins", - atom_to_list(emqx_auth_mnesia), + atom_to_list(emqx_retainer), "load"]), auth_header_()), {ok, Plugins3} = request_api(get, api_path(["nodes", atom_to_list(node()), "plugins"]), auth_header_()), - [Plugin3] = filter(get(<<"data">>, Plugins3), <<"name">>, <<"emqx_auth_mnesia">>), - ?assertEqual(<<"emqx_auth_mnesia">>, maps:get(<<"name">>, Plugin3)), + [Plugin3] = filter(get(<<"data">>, Plugins3), <<"name">>, <<"emqx_retainer">>), + ?assertEqual(<<"emqx_retainer">>, maps:get(<<"name">>, Plugin3)), ?assertEqual(true, maps:get(<<"active">>, Plugin3)), {ok, _} = request_api(put, api_path(["nodes", atom_to_list(node()), "plugins", - atom_to_list(emqx_auth_mnesia), + atom_to_list(emqx_retainer), "unload"]), auth_header_()), {ok, Error2} = request_api(put, api_path(["nodes", atom_to_list(node()), "plugins", - atom_to_list(emqx_auth_mnesia), + atom_to_list(emqx_retainer), "unload"]), auth_header_()), ?assertEqual(<<"not_started">>, get(<<"message">>, Error2)), - application:stop(emqx_auth_mnesia). + application:stop(emqx_retainer). t_acl_cache(_) -> ClientId = <<"client1">>, diff --git a/deploy/docker/README.md b/deploy/docker/README.md index 5702b622c..f243c7a4b 100644 --- a/deploy/docker/README.md +++ b/deploy/docker/README.md @@ -136,16 +136,16 @@ Default environment variable ``EMQX_LOADED_PLUGINS``, including EMQX_LOADED_PLUGINS="emqx_recon,emqx_retainer,emqx_management,emqx_dashboard" ``` -For example, set ``EMQX_LOADED_PLUGINS= emqx_auth_redis,emqx_auth_mysql`` to load these two plugins. +For example, set ``EMQX_LOADED_PLUGINS= emqx_retainer,emqx_rule_engine`` to load these two plugins. You can use comma, space or other separator that you want. All the plugins defined in ``EMQX_LOADED_PLUGINS`` will be loaded. ```bash -EMQX_LOADED_PLUGINS="emqx_auth_redis,emqx_auth_mysql" -EMQX_LOADED_PLUGINS="emqx_auth_redis emqx_auth_mysql" -EMQX_LOADED_PLUGINS="emqx_auth_redis | emqx_auth_mysql" +EMQX_LOADED_PLUGINS="emqx_retainer,emqx_rule_engine" +EMQX_LOADED_PLUGINS="emqx_retainer emqx_rule_engine" +EMQX_LOADED_PLUGINS="emqx_retainer | emqx_rule_engine" ``` #### EMQ X Plugins Configuration @@ -155,8 +155,8 @@ The environment variables which with ``EMQX_`` prefix are mapped to all emqx plu Example: ```bash -EMQX_AUTH__REDIS__SERVER <--> auth.redis.server -EMQX_AUTH__REDIS__PASSWORD <--> auth.redis.password +EMQX_RETAINER__STORAGE_TYPE <--> retainer.storage_type +EMQX_RETAINER__MAX_PAYLOAD_SIZE <--> retainer.max_payload_size ``` Don't worry about where to find the configuration file of emqx plugins, this docker image will find and config them automatically using some magic. @@ -166,15 +166,14 @@ All plugin of emqx project could config in this way, following the environment v Assume you are using redis auth plugin, for example: ```bash -#EMQX_AUTH__REDIS__SERVER="redis.at.yourserver" -#EMQX_AUTH__REDIS__PASSWORD="password_for_redis" +#EMQX_RETAINER__STORAGE_TYPE = "ram" +#EMQX_RETAINER.MAX_PAYLOAD_SIZE = 1MB docker run -d --name emqx -p 18083:18083 -p 1883:1883 -p 4369:4369 \ -e EMQX_LISTENER__TCP__EXTERNAL=1883 \ - -e EMQX_LOADED_PLUGINS="emqx_auth_redis" \ - -e EMQX_AUTH__REDIS__SERVER="your.redis.server:6379" \ - -e EMQX_AUTH__REDIS__PASSWORD="password_for_redis" \ - -e EMQX_AUTH__REDIS__PASSWORD_HASH=plain \ + -e EMQX_LOADED_PLUGINS="emqx_retainer" \ + -e EMQX_RETAINER__STORAGE_TYPE = "ram" \ + -e EMQX_RETAINER__MAX_PAYLOAD_SIZE = 1MB \ emqx/emqx:latest ``` diff --git a/rebar.config.erl b/rebar.config.erl index 88148818e..7a473cc6a 100644 --- a/rebar.config.erl +++ b/rebar.config.erl @@ -279,10 +279,6 @@ relx_plugin_apps(ReleaseType) -> , emqx_coap , emqx_stomp , emqx_authentication - , emqx_auth_http - , emqx_auth_mysql - , emqx_auth_jwt - , emqx_auth_mnesia , emqx_web_hook , emqx_rule_engine , emqx_sasl @@ -294,10 +290,6 @@ relx_plugin_apps(ReleaseType) -> relx_plugin_apps_per_rel(cloud) -> [ emqx_lwm2m - , emqx_auth_ldap - , emqx_auth_pgsql - , emqx_auth_redis - , emqx_auth_mongo , emqx_lua_hook , emqx_exhook , emqx_exproto From ecc450e94227766ca174a0df40aebb1cee5b270a Mon Sep 17 00:00:00 2001 From: Rory Z Date: Fri, 25 Jun 2021 10:58:16 +0800 Subject: [PATCH 029/174] chore: delete import and export feature --- apps/emqx_authentication/rebar.config | 3 +- apps/emqx_connector/rebar.config | 16 +- .../src/emqx_mgmt_api_data.erl | 186 ----- apps/emqx_management/src/emqx_mgmt_cli.erl | 31 - .../src/emqx_mgmt_data_backup.erl | 739 ------------------ .../test/emqx_auth_mnesia_migration_SUITE.erl | 176 ----- .../make_data.sh | 134 ---- .../v4.0.11-no-auth.json | 23 - .../v4.0.11.json | 28 - .../v4.0.13.json | 37 - .../v4.1.5.json | 58 -- .../v4.2.10-no-auth.json | 29 - .../v4.2.10.json | 53 -- .../v4.2.11.json | 58 -- rebar.config | 1 - 15 files changed, 16 insertions(+), 1556 deletions(-) delete mode 100644 apps/emqx_management/src/emqx_mgmt_api_data.erl delete mode 100644 apps/emqx_management/src/emqx_mgmt_data_backup.erl delete mode 100644 apps/emqx_management/test/emqx_auth_mnesia_migration_SUITE.erl delete mode 100755 apps/emqx_management/test/emqx_auth_mnesia_migration_SUITE_data/make_data.sh delete mode 100644 apps/emqx_management/test/emqx_auth_mnesia_migration_SUITE_data/v4.0.11-no-auth.json delete mode 100644 apps/emqx_management/test/emqx_auth_mnesia_migration_SUITE_data/v4.0.11.json delete mode 100644 apps/emqx_management/test/emqx_auth_mnesia_migration_SUITE_data/v4.0.13.json delete mode 100644 apps/emqx_management/test/emqx_auth_mnesia_migration_SUITE_data/v4.1.5.json delete mode 100644 apps/emqx_management/test/emqx_auth_mnesia_migration_SUITE_data/v4.2.10-no-auth.json delete mode 100644 apps/emqx_management/test/emqx_auth_mnesia_migration_SUITE_data/v4.2.10.json delete mode 100644 apps/emqx_management/test/emqx_auth_mnesia_migration_SUITE_data/v4.2.11.json diff --git a/apps/emqx_authentication/rebar.config b/apps/emqx_authentication/rebar.config index 89bd1f54a..73696b033 100644 --- a/apps/emqx_authentication/rebar.config +++ b/apps/emqx_authentication/rebar.config @@ -1,5 +1,4 @@ -{deps, [{jose, {git, "https://github.com/potatosalad/erlang-jose", {tag, "1.11.1"}}} - ]}. +{deps, []}. {edoc_opts, [{preprocess, true}]}. {erl_opts, [warn_unused_vars, diff --git a/apps/emqx_connector/rebar.config b/apps/emqx_connector/rebar.config index 53b5c63e8..633b427cf 100644 --- a/apps/emqx_connector/rebar.config +++ b/apps/emqx_connector/rebar.config @@ -4,7 +4,21 @@ ]}. {deps, [ - {mysql, {git, "https://github.com/emqx/mysql-otp", {tag, "1.7.1"}}} + {jose, {git, "https://github.com/potatosalad/erlang-jose", {tag, "1.11.1"}}}, + {eldap2, {git, "https://github.com/emqx/eldap2", {tag, "v0.2.2"}}}, + {mysql, {git, "https://github.com/emqx/mysql-otp", {tag, "1.7.1"}}}, + {epgsql, {git, "https://github.com/epgsql/epgsql", {tag, "4.4.0"}}}, + %% NOTE: mind poolboy version when updating mongodb-erlang version + {mongodb, {git,"https://github.com/emqx/mongodb-erlang", {tag, "v3.0.7"}}}, + %% NOTE: mind poolboy version when updating eredis_cluster version + {eredis_cluster, {git, "https://github.com/emqx/eredis_cluster", {tag, "0.6.6"}}}, + %% mongodb-erlang uses a special fork https://github.com/comtihon/poolboy.git + %% (which has overflow_ttl feature added). + %% However, it references `{branch, "master}` (commit 9c06a9a on 2021-04-07). + %% By accident, We have always been using the upstream fork due to + %% eredis_cluster's dependency getting resolved earlier. + %% Here we pin 1.5.2 to avoid surprises in the future. + {poolboy, {git, "https://github.com/emqx/poolboy.git", {tag, "1.5.2"}}} ]}. {shell, [ diff --git a/apps/emqx_management/src/emqx_mgmt_api_data.erl b/apps/emqx_management/src/emqx_mgmt_api_data.erl deleted file mode 100644 index e389a1313..000000000 --- a/apps/emqx_management/src/emqx_mgmt_api_data.erl +++ /dev/null @@ -1,186 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_mgmt_api_data). - --include_lib("emqx/include/emqx.hrl"). - --include_lib("kernel/include/file.hrl"). - --include("emqx_mgmt.hrl"). - --rest_api(#{name => export, - method => 'POST', - path => "/data/export", - func => export, - descr => "Export data"}). - --rest_api(#{name => list_exported, - method => 'GET', - path => "/data/export", - func => list_exported, - descr => "List exported file"}). - --rest_api(#{name => import, - method => 'POST', - path => "/data/import", - func => import, - descr => "Import data"}). - --rest_api(#{name => download, - method => 'GET', - path => "/data/file/:filename", - func => download, - descr => "Download data file to local"}). - --rest_api(#{name => upload, - method => 'POST', - path => "/data/file", - func => upload, - descr => "Upload data file from local"}). - --rest_api(#{name => delete, - method => 'DELETE', - path => "/data/file/:filename", - func => delete, - descr => "Delete data file"}). - --export([ export/2 - , list_exported/2 - , import/2 - , download/2 - , upload/2 - , delete/2 - ]). - --export([ get_list_exported/0 - , do_import/1 - ]). - -export(_Bindings, _Params) -> - case emqx_mgmt_data_backup:export() of - {ok, File = #{filename := Filename}} -> - minirest:return({ok, File#{filename => filename:basename(Filename)}}); - Return -> minirest:return(Return) - end. - -list_exported(_Bindings, _Params) -> - List = [ rpc:call(Node, ?MODULE, get_list_exported, []) || Node <- ekka_mnesia:running_nodes() ], - NList = lists:map(fun({_, FileInfo}) -> FileInfo end, lists:keysort(1, lists:append(List))), - minirest:return({ok, NList}). - -get_list_exported() -> - Dir = emqx:get_env(data_dir), - {ok, Files} = file:list_dir_all(Dir), - lists:foldl( - fun(File, Acc) -> - case filename:extension(File) =:= ".json" of - true -> - FullFile = filename:join([Dir, File]), - case file:read_file_info(FullFile) of - {ok, #file_info{size = Size, ctime = CTime = {{Y, M, D}, {H, MM, S}}}} -> - CreatedAt = io_lib:format("~p-~p-~p ~p:~p:~p", [Y, M, D, H, MM, S]), - Seconds = calendar:datetime_to_gregorian_seconds(CTime), - [{Seconds, [{filename, list_to_binary(File)}, - {size, Size}, - {created_at, list_to_binary(CreatedAt)}, - {node, node()} - ]} | Acc]; - {error, Reason} -> - logger:error("Read file info of ~s failed with: ~p", [File, Reason]), - Acc - end; - false -> Acc - end - end, [], Files). - -import(_Bindings, Params) -> - case proplists:get_value(<<"filename">>, Params) of - undefined -> - Result = import_content(Params), - minirest:return(Result); - Filename -> - case proplists:get_value(<<"node">>, Params) of - undefined -> - Result = do_import(Filename), - minirest:return(Result); - Node -> - case lists:member(Node, - [ erlang:atom_to_binary(N, utf8) || N <- ekka_mnesia:running_nodes() ] - ) of - true -> minirest:return(rpc:call(erlang:binary_to_atom(Node, utf8), ?MODULE, do_import, [Filename])); - false -> minirest:return({error, no_existent_node}) - end - end - end. - -do_import(Filename) -> - FullFilename = fullname(Filename), - emqx_mgmt_data_backup:import(FullFilename, "{}"). - -download(#{filename := Filename}, _Params) -> - FullFilename = fullname(Filename), - case file:read_file(FullFilename) of - {ok, Bin} -> - {ok, #{filename => list_to_binary(Filename), - file => Bin}}; - {error, Reason} -> - minirest:return({error, Reason}) - end. - -upload(Bindings, Params) -> - do_upload(Bindings, maps:from_list(Params)). - -do_upload(_Bindings, #{<<"filename">> := Filename, - <<"file">> := Bin}) -> - FullFilename = fullname(Filename), - case file:write_file(FullFilename, Bin) of - ok -> - minirest:return({ok, [{node, node()}]}); - {error, Reason} -> - minirest:return({error, Reason}) - end; -do_upload(Bindings, Params = #{<<"file">> := _}) -> - do_upload(Bindings, Params#{<<"filename">> => tmp_filename()}); -do_upload(_Bindings, _Params) -> - minirest:return({error, missing_required_params}). - -delete(#{filename := Filename}, _Params) -> - FullFilename = fullname(Filename), - case file:delete(FullFilename) of - ok -> - minirest:return(); - {error, Reason} -> - minirest:return({error, Reason}) - end. - -import_content(Content) -> - File = dump_to_tmp_file(Content), - do_import(File). - -dump_to_tmp_file(Content) -> - Bin = emqx_json:encode(Content), - Filename = tmp_filename(), - ok = file:write_file(fullname(Filename), Bin), - Filename. - -fullname(Name) -> - filename:join(emqx:get_env(data_dir), Name). - -tmp_filename() -> - Seconds = erlang:system_time(second), - {{Y, M, D}, {H, MM, S}} = emqx_mgmt_util:datetime(Seconds), - io_lib:format("emqx-export-~p-~p-~p-~p-~p-~p.json", [Y, M, D, H, MM, S]). diff --git a/apps/emqx_management/src/emqx_mgmt_cli.erl b/apps/emqx_management/src/emqx_mgmt_cli.erl index 77fe96182..e70def3ee 100644 --- a/apps/emqx_management/src/emqx_mgmt_cli.erl +++ b/apps/emqx_management/src/emqx_mgmt_cli.erl @@ -38,7 +38,6 @@ , trace/1 , log/1 , mgmt/1 - , data/1 , acl/1 ]). @@ -544,36 +543,6 @@ stop_listener(#{listen_on := ListenOn} = Listener, _Input) -> [ID, ListenOnStr, Reason]) end. -%%-------------------------------------------------------------------- -%% @doc data Command - -data(["export"]) -> - case emqx_mgmt_data_backup:export() of - {ok, #{filename := Filename}} -> - emqx_ctl:print("The emqx data has been successfully exported to ~s.~n", [Filename]); - {error, Reason} -> - emqx_ctl:print("The emqx data export failed due to ~p.~n", [Reason]) - end; - -data(["import", Filename]) -> - data(["import", Filename, "--env", "{}"]); -data(["import", Filename, "--env", Env]) -> - case emqx_mgmt_data_backup:import(Filename, Env) of - ok -> - emqx_ctl:print("The emqx data has been imported successfully.~n"); - {error, import_failed} -> - emqx_ctl:print("The emqx data import failed.~n"); - {error, unsupported_version} -> - emqx_ctl:print("The emqx data import failed: Unsupported version.~n"); - {error, Reason} -> - emqx_ctl:print("The emqx data import failed: ~0p while reading ~s.~n", [Reason, Filename]) - end; - -data(_) -> - emqx_ctl:usage([{"data import [--env '']", - "Import data from the specified file, possibly with overrides"}, - {"data export", "Export data"}]). - %%-------------------------------------------------------------------- %% @doc acl Command diff --git a/apps/emqx_management/src/emqx_mgmt_data_backup.erl b/apps/emqx_management/src/emqx_mgmt_data_backup.erl deleted file mode 100644 index 71a92686e..000000000 --- a/apps/emqx_management/src/emqx_mgmt_data_backup.erl +++ /dev/null @@ -1,739 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_mgmt_data_backup). - --include("emqx_mgmt.hrl"). --include_lib("emqx_rule_engine/include/rule_engine.hrl"). --include_lib("emqx/include/emqx.hrl"). --include_lib("kernel/include/file.hrl"). - --ifdef(EMQX_ENTERPRISE). --export([ export_modules/0 - , export_schemas/0 - , export_confs/0 - , import_modules/1 - , import_schemas/1 - , import_confs/2 - ]). --endif. - --export([ export_rules/0 - , export_resources/0 - , export_blacklist/0 - , export_applications/0 - , export_users/0 - , export_auth_mnesia/0 - , export_acl_mnesia/0 - , import_resources_and_rules/3 - , import_rules/1 - , import_resources/1 - , import_blacklist/1 - , import_applications/1 - , import_users/1 - , import_auth_clientid/1 %% BACKW: 4.1.x - , import_auth_username/1 %% BACKW: 4.1.x - , import_auth_mnesia/2 - , import_acl_mnesia/2 - , to_version/1 - ]). - --export([ export/0 - , import/2 - ]). - -%%-------------------------------------------------------------------- -%% Data Export and Import -%%-------------------------------------------------------------------- - -export_rules() -> - lists:map(fun(#rule{id = RuleId, - rawsql = RawSQL, - actions = Actions, - enabled = Enabled, - description = Desc}) -> - [{id, RuleId}, - {rawsql, RawSQL}, - {actions, actions_to_prop_list(Actions)}, - {enabled, Enabled}, - {description, Desc}] - end, emqx_rule_registry:get_rules()). - -export_resources() -> - lists:map(fun(#resource{id = Id, - type = Type, - config = Config, - created_at = CreatedAt, - description = Desc}) -> - NCreatedAt = case CreatedAt of - undefined -> null; - _ -> CreatedAt - end, - [{id, Id}, - {type, Type}, - {config, maps:to_list(Config)}, - {created_at, NCreatedAt}, - {description, Desc}] - end, emqx_rule_registry:get_resources()). - -export_blacklist() -> - lists:map(fun(#banned{who = Who, by = By, reason = Reason, at = At, until = Until}) -> - NWho = case Who of - {peerhost, Peerhost} -> {peerhost, inet:ntoa(Peerhost)}; - _ -> Who - end, - [{who, [NWho]}, {by, By}, {reason, Reason}, {at, At}, {until, Until}] - end, ets:tab2list(emqx_banned)). - -export_applications() -> - lists:map(fun({_, AppID, AppSecret, Name, Desc, Status, Expired}) -> - [{id, AppID}, {secret, AppSecret}, {name, Name}, {desc, Desc}, {status, Status}, {expired, Expired}] - end, ets:tab2list(mqtt_app)). - -export_users() -> - lists:map(fun({_, Username, Password, Tags}) -> - [{username, Username}, {password, base64:encode(Password)}, {tags, Tags}] - end, ets:tab2list(mqtt_admin)). - -export_auth_mnesia() -> - case ets:info(emqx_user) of - undefined -> []; - _ -> - lists:map(fun({_, {Type, Login}, Password, CreatedAt}) -> - [{login, Login}, {type, Type}, {password, base64:encode(Password)}, {created_at, CreatedAt}] - end, ets:tab2list(emqx_user)) - end. - -export_acl_mnesia() -> - case ets:info(emqx_acl) of - undefined -> []; - _ -> - lists:map(fun({_, Filter, Action, Access, CreatedAt}) -> - Filter1 = case Filter of - {{Type, TypeValue}, Topic} -> - [{type, Type}, {type_value, TypeValue}, {topic, Topic}]; - {Type, Topic} -> - [{type, Type}, {topic, Topic}] - end, - Filter1 ++ [{action, Action}, {access, Access}, {created_at, CreatedAt}] - end, ets:tab2list(emqx_acl)) - end. - --ifdef(EMQX_ENTERPRISE). -export_modules() -> - case ets:info(emqx_modules) of - undefined -> []; - _ -> - lists:map(fun({_, Id, Type, Config, Enabled, CreatedAt, Description}) -> - [{id, Id}, - {type, Type}, - {config, Config}, - {enabled, Enabled}, - {created_at, CreatedAt}, - {description, Description} - ] - end, ets:tab2list(emqx_modules)) - end. - -export_schemas() -> - case ets:info(emqx_schema) of - undefined -> []; - _ -> - [emqx_schema_api:format_schema(Schema) || Schema <- emqx_schema_registry:get_all_schemas()] - end. - -export_confs() -> - case ets:info(emqx_conf_info) of - undefined -> {[], []}; - _ -> - {lists:map(fun({_, Key, Confs}) -> - case Key of - {_Zone, Name} -> - [{zone, list_to_binary(Name)}, - {confs, confs_to_binary(Confs)}]; - {_Listener, Type, Name} -> - [{type, list_to_binary(Type)}, - {name, list_to_binary(Name)}, - {confs, confs_to_binary(Confs)}]; - Name -> - [{name, list_to_binary(Name)}, - {confs, confs_to_binary(Confs)}] - end - end, ets:tab2list(emqx_conf_b)), - lists:map(fun({_, {_Listener, Type, Name}, Status}) -> - [{type, list_to_binary(Type)}, - {name, list_to_binary(Name)}, - {status, Status}] - end, ets:tab2list(emqx_listeners_state))} - end. - -confs_to_binary(Confs) -> - [{list_to_binary(Key), list_to_binary(Val)} || {Key, Val} <-Confs]. - --endif. - -import_rule(#{<<"id">> := RuleId, - <<"rawsql">> := RawSQL, - <<"actions">> := Actions, - <<"enabled">> := Enabled, - <<"description">> := Desc}) -> - Rule = #{id => RuleId, - rawsql => RawSQL, - actions => map_to_actions(Actions), - enabled => Enabled, - description => Desc}, - try emqx_rule_engine:create_rule(Rule) - catch throw:{resource_not_initialized, _ResId} -> - emqx_rule_engine:create_rule(Rule#{enabled => false}) - end. - -map_to_actions(Maps) -> - [map_to_action(M) || M <- Maps]. - -map_to_action(Map = #{<<"id">> := ActionInstId, <<"name">> := Name, <<"args">> := Args}) -> - #{id => ActionInstId, - name => any_to_atom(Name), - args => Args, - fallbacks => map_to_actions(maps:get(<<"fallbacks">>, Map, []))}. - - -import_rules(Rules) -> - lists:foreach(fun(Rule) -> - import_rule(Rule) - end, Rules). - -import_resources(Reources) -> - lists:foreach(fun(Resource) -> - import_resource(Resource) - end, Reources). - -import_resource(#{<<"id">> := Id, - <<"type">> := Type, - <<"config">> := Config, - <<"created_at">> := CreatedAt, - <<"description">> := Desc}) -> - NCreatedAt = case CreatedAt of - null -> undefined; - _ -> CreatedAt - end, - emqx_rule_engine:create_resource(#{id => Id, - type => any_to_atom(Type), - config => Config, - created_at => NCreatedAt, - description => Desc}). -import_resources_and_rules(Resources, Rules, FromVersion) - when FromVersion =:= "4.0" orelse - FromVersion =:= "4.1" orelse - FromVersion =:= "4.2" -> - Configs = lists:foldl(fun compatible_version/2 , [], Resources), - lists:foreach(fun(#{<<"actions">> := Actions} = Rule) -> - NActions = apply_new_config(Actions, Configs), - import_rule(Rule#{<<"actions">> := NActions}) - end, Rules); -import_resources_and_rules(Resources, Rules, _FromVersion) -> - import_resources(Resources), - import_rules(Rules). - -%% 4.2.5 + -compatible_version(#{<<"id">> := ID, - <<"type">> := <<"web_hook">>, - <<"config">> := #{<<"connect_timeout">> := ConnectTimeout, - <<"content_type">> := ContentType, - <<"headers">> := Headers, - <<"method">> := Method, - <<"pool_size">> := PoolSize, - <<"request_timeout">> := RequestTimeout, - <<"url">> := URL}} = Resource, Acc) -> - CovertFun = fun(Int) -> - list_to_binary(integer_to_list(Int) ++ "s") - end, - Cfg = make_new_config(#{<<"pool_size">> => PoolSize, - <<"connect_timeout">> => CovertFun(ConnectTimeout), - <<"request_timeout">> => CovertFun(RequestTimeout), - <<"url">> => URL}), - {ok, _Resource} = import_resource(Resource#{<<"config">> := Cfg}), - NHeaders = maps:put(<<"content-type">>, ContentType, covert_empty_headers(Headers)), - [{ID, #{headers => NHeaders, method => Method}} | Acc]; -% 4.2.0 -compatible_version(#{<<"id">> := ID, - <<"type">> := <<"web_hook">>, - <<"config">> := #{<<"headers">> := Headers, - <<"method">> := Method,%% 4.2.0 Different here - <<"url">> := URL}} = Resource, Acc) -> - Cfg = make_new_config(#{<<"url">> => URL}), - {ok, _Resource} = import_resource(Resource#{<<"config">> := Cfg}), - NHeaders = maps:put(<<"content-type">>, <<"application/json">> , covert_empty_headers(Headers)), - [{ID, #{headers => NHeaders, method => Method}} | Acc]; - -%% bridge mqtt -%% 4.2.0 - 4.2.5 bridge_mqtt, ssl enabled from on/off to true/false -compatible_version(#{<<"type">> := <<"bridge_mqtt">>, - <<"id">> := ID, %% begin 4.2.0. - <<"config">> := #{<<"ssl">> := Ssl} = Config} = Resource, Acc) -> - NewConfig = Config#{<<"ssl">> := flag_to_boolean(Ssl), - <<"pool_size">> => case maps:get(<<"pool_size">>, Config, undefined) of %% 4.0.x, compatible `pool_size` - undefined -> 8; - PoolSize -> PoolSize - end}, - {ok, _Resource} = import_resource(Resource#{<<"config">> := NewConfig}), - [{ID, NewConfig} | Acc]; - -% 4.2.3, add :content_type -compatible_version(#{<<"id">> := ID, - <<"type">> := <<"web_hook">>, - <<"config">> := #{<<"headers">> := Headers, - <<"content_type">> := ContentType,%% 4.2.3 Different here - <<"method">> := Method, - <<"url">> := URL}} = Resource, Acc) -> - Cfg = make_new_config(#{<<"url">> => URL}), - {ok, _Resource} = import_resource(Resource#{<<"config">> := Cfg}), - NHeaders = maps:put(<<"content-type">>, ContentType, covert_empty_headers(Headers)), - [{ID, #{headers => NHeaders, method => Method}} | Acc]; -% normal version -compatible_version(Resource, Acc) -> - {ok, _Resource} = import_resource(Resource), - Acc. - -make_new_config(Cfg) -> - Config = #{<<"pool_size">> => 8, - <<"connect_timeout">> => <<"5s">>, - <<"request_timeout">> => <<"5s">>, - <<"cacertfile">> => <<>>, - <<"certfile">> => <<>>, - <<"keyfile">> => <<>>, - <<"verify">> => false}, - maps:merge(Cfg, Config). - -apply_new_config(Actions, Configs) -> - apply_new_config(Actions, Configs, []). - -apply_new_config([], _Configs, Acc) -> - Acc; -apply_new_config(Actions, [], []) -> - Actions; -apply_new_config([Action = #{<<"name">> := <<"data_to_webserver">>, - <<"args">> := #{<<"$resource">> := ID, - <<"path">> := Path, - <<"payload_tmpl">> := PayloadTmpl}} | More], Configs, Acc) -> - case proplists:get_value(ID, Configs, undefined) of - undefined -> - apply_new_config(More, Configs, [Action | Acc]); - #{headers := Headers, method := Method} -> - Args = #{<<"$resource">> => ID, - <<"body">> => PayloadTmpl, - <<"headers">> => Headers, - <<"method">> => Method, - <<"path">> => Path}, - apply_new_config(More, Configs, [Action#{<<"args">> := Args} | Acc]) - end; - -apply_new_config([Action = #{<<"args">> := #{<<"$resource">> := ResourceId, - <<"forward_topic">> := ForwardTopic, - <<"payload_tmpl">> := PayloadTmpl}, - <<"fallbacks">> := _Fallbacks, - <<"id">> := _Id, - <<"name">> := <<"data_to_mqtt_broker">>} | More], Configs, Acc) -> - Args = #{<<"$resource">> => ResourceId, - <<"payload_tmpl">> => PayloadTmpl, - <<"forward_topic">> => ForwardTopic}, - apply_new_config(More, Configs, [Action#{<<"args">> := Args} | Acc]). - - -actions_to_prop_list(Actions) -> - [action_to_prop_list(Act) || Act <- Actions]. - -action_to_prop_list({action_instance, ActionInstId, Name, FallbackActions, Args}) -> - [{id, ActionInstId}, - {name, Name}, - {fallbacks, actions_to_prop_list(FallbackActions)}, - {args, Args}]. - -import_blacklist(Blacklist) -> - lists:foreach(fun(#{<<"who">> := Who, - <<"by">> := By, - <<"reason">> := Reason, - <<"at">> := At, - <<"until">> := Until}) -> - NWho = case Who of - #{<<"peerhost">> := Peerhost} -> - {ok, NPeerhost} = inet:parse_address(Peerhost), - {peerhost, NPeerhost}; - #{<<"clientid">> := ClientId} -> {clientid, ClientId}; - #{<<"username">> := Username} -> {username, Username} - end, - emqx_banned:create(#banned{who = NWho, by = By, reason = Reason, at = At, until = Until}) - end, Blacklist). - -import_applications(Apps) -> - lists:foreach(fun(#{<<"id">> := AppID, - <<"secret">> := AppSecret, - <<"name">> := Name, - <<"desc">> := Desc, - <<"status">> := Status, - <<"expired">> := Expired}) -> - NExpired = case is_integer(Expired) of - true -> Expired; - false -> undefined - end, - emqx_mgmt_auth:force_add_app(AppID, Name, AppSecret, Desc, Status, NExpired) - end, Apps). - -import_users(Users) -> - lists:foreach(fun(#{<<"username">> := Username, - <<"password">> := Password, - <<"tags">> := Tags}) -> - NPassword = base64:decode(Password), - emqx_dashboard_admin:force_add_user(Username, NPassword, Tags) - end, Users). - -import_auth_clientid(Lists) -> - case ets:info(emqx_user) of - undefined -> ok; - _ -> - lists:foreach(fun(#{<<"clientid">> := Clientid, <<"password">> := Password}) -> - mnesia:dirty_write({emqx_user, {clientid, Clientid} - , base64:decode(Password) - , erlang:system_time(millisecond)}) - end, Lists) - end. - -import_auth_username(Lists) -> - case ets:info(emqx_user) of - undefined -> ok; - _ -> - lists:foreach(fun(#{<<"username">> := Username, <<"password">> := Password}) -> - mnesia:dirty_write({emqx_user, {username, Username}, base64:decode(Password), erlang:system_time(millisecond)}) - end, Lists) - end. - --ifdef(EMQX_ENTERPRISE). -import_auth_mnesia(Auths, FromVersion) when FromVersion =:= "4.0" orelse - FromVersion =:= "4.1" -> - do_import_auth_mnesia_by_old_data(Auths); -import_auth_mnesia(Auths, _) -> - do_import_auth_mnesia(Auths). - -import_acl_mnesia(Acls, FromVersion) when FromVersion =:= "4.0" orelse - FromVersion =:= "4.1" -> - do_import_acl_mnesia_by_old_data(Acls); - -import_acl_mnesia(Acls, _) -> - do_import_acl_mnesia(Acls). --else. -import_auth_mnesia(Auths, FromVersion) when FromVersion =:= "4.3" -> - do_import_auth_mnesia(Auths); -import_auth_mnesia(Auths, _FromVersion) -> - do_import_auth_mnesia_by_old_data(Auths). - -import_acl_mnesia(Acls, FromVersion) when FromVersion =:= "4.3" -> - do_import_acl_mnesia(Acls); -import_acl_mnesia(Acls, _FromVersion) -> - do_import_acl_mnesia_by_old_data(Acls). - --endif. - -do_import_auth_mnesia_by_old_data(Auths) -> - case ets:info(emqx_user) of - undefined -> ok; - _ -> - CreatedAt = erlang:system_time(millisecond), - lists:foreach(fun(#{<<"login">> := Login, - <<"password">> := Password}) -> - mnesia:dirty_write({emqx_user, {get_old_type(), Login}, base64:decode(Password), CreatedAt}) - end, Auths) - end. - - -do_import_auth_mnesia(Auths) -> - case ets:info(emqx_user) of - undefined -> ok; - _ -> - lists:foreach(fun(#{<<"login">> := Login, - <<"type">> := Type, - <<"password">> := Password } = Map) -> - CreatedAt = maps:get(<<"created_at">>, Map, erlang:system_time(millisecond)), - mnesia:dirty_write({emqx_user, {any_to_atom(Type), Login}, base64:decode(Password), CreatedAt}) - end, Auths) - end. - -do_import_acl_mnesia_by_old_data(Acls) -> - case ets:info(emqx_acl) of - undefined -> ok; - _ -> - CreatedAt = erlang:system_time(millisecond), - lists:foreach(fun(#{<<"login">> := Login, - <<"topic">> := Topic, - <<"allow">> := Allow, - <<"action">> := Action}) -> - Allow1 = case any_to_atom(Allow) of - true -> allow; - false -> deny - end, - mnesia:dirty_write({emqx_acl, {{get_old_type(), Login}, Topic}, any_to_atom(Action), Allow1, CreatedAt}) - end, Acls) - end. -do_import_acl_mnesia(Acls) -> - case ets:info(emqx_acl) of - undefined -> ok; - _ -> - lists:foreach(fun(Map = #{<<"action">> := Action, - <<"access">> := Access}) -> - Topic = maps:get(<<"topic">>, Map), - Login = case maps:get(<<"type_value">>, Map, undefined) of - undefined -> - all; - Value -> - {any_to_atom(maps:get(<<"type">>, Map)), Value} - end, - emqx_acl_mnesia_cli:add_acl(Login, Topic, any_to_atom(Action), any_to_atom(Access)) - end, Acls) - end. - --ifdef(EMQX_ENTERPRISE). --dialyzer({nowarn_function, [import_modules/1]}). -import_modules(Modules) -> - case ets:info(emqx_modules) of - undefined -> - ok; - _ -> - lists:foreach(fun(#{<<"id">> := Id, - <<"type">> := Type, - <<"config">> := Config, - <<"enabled">> := Enabled, - <<"created_at">> := CreatedAt, - <<"description">> := Description}) -> - _ = emqx_modules:import_module({Id, any_to_atom(Type), Config, Enabled, CreatedAt, Description}) - end, Modules) - end. - - -import_schemas(Schemas) -> - case ets:info(emqx_schema) of - undefined -> ok; - _ -> [emqx_schema_registry:add_schema(emqx_schema_api:make_schema_params(Schema)) || Schema <- Schemas] - end. - -import_confs(Configs, ListenersState) -> - case ets:info(emqx_conf_info) of - undefined -> ok; - _ -> - emqx_conf:import_confs(Configs, ListenersState) - end. - --endif. - -any_to_atom(L) when is_list(L) -> list_to_atom(L); -any_to_atom(B) when is_binary(B) -> binary_to_atom(B, utf8); -any_to_atom(A) when is_atom(A) -> A. - -to_version(Version) when is_integer(Version) -> - integer_to_list(Version); -to_version(Version) when is_binary(Version) -> - binary_to_list(Version); -to_version(Version) when is_list(Version) -> - Version. - -export() -> - Seconds = erlang:system_time(second), - Data = do_export_data() ++ [{date, erlang:list_to_binary(emqx_mgmt_util:strftime(Seconds))}], - {{Y, M, D}, {H, MM, S}} = emqx_mgmt_util:datetime(Seconds), - Filename = io_lib:format("emqx-export-~p-~p-~p-~p-~p-~p.json", [Y, M, D, H, MM, S]), - NFilename = filename:join([emqx:get_env(data_dir), Filename]), - ok = filelib:ensure_dir(NFilename), - case file:write_file(NFilename, emqx_json:encode(Data)) of - ok -> - case file:read_file_info(NFilename) of - {ok, #file_info{size = Size, ctime = {{Y1, M1, D1}, {H1, MM1, S1}}}} -> - CreatedAt = io_lib:format("~p-~p-~p ~p:~p:~p", [Y1, M1, D1, H1, MM1, S1]), - {ok, #{filename => list_to_binary(NFilename), - size => Size, - created_at => list_to_binary(CreatedAt), - node => node() - }}; - Error -> Error - end; - Error -> Error - end. - -do_export_data() -> - Version = string:sub_string(emqx_sys:version(), 1, 3), - [{version, erlang:list_to_binary(Version)}, - {rules, export_rules()}, - {resources, export_resources()}, - {blacklist, export_blacklist()}, - {apps, export_applications()}, - {users, export_users()}, - {auth_mnesia, export_auth_mnesia()}, - {acl_mnesia, export_acl_mnesia()} - ] ++ do_export_extra_data(). - --ifdef(EMQX_ENTERPRISE). -do_export_extra_data() -> - {Configs, State} = export_confs(), - [{modules, export_modules()}, - {schemas, export_schemas()}, - {configs, Configs}, - {listeners_state, State} - ]. --else. -do_export_extra_data() -> []. --endif. - --ifdef(EMQX_ENTERPRISE). -import(Filename, OverridesJson) -> - case file:read_file(Filename) of - {ok, Json} -> - Imported = emqx_json:decode(Json, [return_maps]), - Overrides = emqx_json:decode(OverridesJson, [return_maps]), - Data = maps:merge(Imported, Overrides), - Version = to_version(maps:get(<<"version">>, Data)), - read_global_auth_type(Data), - try - do_import_data(Data, Version), - logger:debug("The emqx data has been imported successfully"), - ok - catch Class:Reason:Stack -> - logger:error("The emqx data import failed: ~0p", [{Class, Reason, Stack}]), - {error, import_failed} - end; - Error -> Error - end. --else. -import(Filename, OverridesJson) -> - case file:read_file(Filename) of - {ok, Json} -> - Imported = emqx_json:decode(Json, [return_maps]), - Overrides = emqx_json:decode(OverridesJson, [return_maps]), - Data = maps:merge(Imported, Overrides), - Version = to_version(maps:get(<<"version">>, Data)), - read_global_auth_type(Data), - case is_version_supported(Data, Version) of - true -> - try - do_import_data(Data, Version), - logger:debug("The emqx data has been imported successfully"), - ok - catch Class:Reason:Stack -> - logger:error("The emqx data import failed: ~0p", [{Class, Reason, Stack}]), - {error, import_failed} - end; - false -> - logger:error("Unsupported version: ~p", [Version]), - {error, unsupported_version, Version} - end; - Error -> Error - end. --endif. - -do_import_data(Data, Version) -> - do_import_extra_data(Data, Version), - import_resources_and_rules(maps:get(<<"resources">>, Data, []), maps:get(<<"rules">>, Data, []), Version), - import_blacklist(maps:get(<<"blacklist">>, Data, [])), - import_applications(maps:get(<<"apps">>, Data, [])), - import_users(maps:get(<<"users">>, Data, [])), - import_auth_clientid(maps:get(<<"auth_clientid">>, Data, [])), - import_auth_username(maps:get(<<"auth_username">>, Data, [])), - import_auth_mnesia(maps:get(<<"auth_mnesia">>, Data, []), Version), - import_acl_mnesia(maps:get(<<"acl_mnesia">>, Data, []), Version). - --ifdef(EMQX_ENTERPRISE). -do_import_extra_data(Data, _Version) -> - _ = import_confs(maps:get(<<"configs">>, Data, []), maps:get(<<"listeners_state">>, Data, [])), - _ = import_modules(maps:get(<<"modules">>, Data, [])), - _ = import_schemas(maps:get(<<"schemas">>, Data, [])), - ok. --else. -do_import_extra_data(_Data, _Version) -> ok. --endif. - -covert_empty_headers([]) -> #{}; -covert_empty_headers(Other) -> Other. - -flag_to_boolean(<<"on">>) -> true; -flag_to_boolean(<<"off">>) -> false; -flag_to_boolean(Other) -> Other. - --ifndef(EMQX_ENTERPRISE). -is_version_supported(Data, Version) -> - case { maps:get(<<"auth_clientid">>, Data, []) - , maps:get(<<"auth_username">>, Data, []) - , maps:get(<<"auth_mnesia">>, Data, [])} of - {[], [], []} -> lists:member(Version, ?VERSIONS); - _ -> is_version_supported2(Version) - end. - -is_version_supported2("4.1") -> - true; -is_version_supported2("4.3") -> - true; -is_version_supported2(Version) -> - case re:run(Version, "^4.[02].\\d+$", [{capture, none}]) of - match -> - try lists:map(fun erlang:list_to_integer/1, string:tokens(Version, ".")) of - [4, 2, N] -> N >= 11; - [4, 0, N] -> N >= 13; - _ -> false - catch - _ : _ -> false - end; - nomatch -> - false - end. --endif. - -read_global_auth_type(Data) -> - case {maps:get(<<"auth_mnesia">>, Data, []), maps:get(<<"acl_mnesia">>, Data, [])} of - {[], []} -> - %% Auth mnesia plugin is not used: - ok; - _ -> - do_read_global_auth_type(Data) - end. - --ifdef(EMQX_ENTERPRISE). -do_read_global_auth_type(Data) -> - case Data of - #{<<"auth.mnesia.as">> := <<"username">>} -> - application:set_env(emqx_auth_mnesia, as, username); - #{<<"auth.mnesia.as">> := <<"clientid">>} -> - application:set_env(emqx_auth_mnesia, as, clientid); - _ -> - ok - end. - --else. -do_read_global_auth_type(Data) -> - case Data of - #{<<"auth.mnesia.as">> := <<"username">>} -> - application:set_env(emqx_auth_mnesia, as, username); - #{<<"auth.mnesia.as">> := <<"clientid">>} -> - application:set_env(emqx_auth_mnesia, as, clientid); - _ -> - logger:error("While importing data from EMQX versions prior to 4.3 " - "it is necessary to specify the value of \"auth.mnesia.as\" parameter " - "as it was configured in etc/plugins/emqx_auth_mnesia.conf.\n" - "Use the following command to import data:\n" - " $ emqx_ctl data import --env '{\"auth.mnesia.as\":\"username\"}'\n" - "or\n" - " $ emqx_ctl data import --env '{\"auth.mnesia.as\":\"clientid\"}'", - []), - error(import_failed) - end. --endif. - -get_old_type() -> - {ok, Type} = application:get_env(emqx_auth_mnesia, as), - Type. diff --git a/apps/emqx_management/test/emqx_auth_mnesia_migration_SUITE.erl b/apps/emqx_management/test/emqx_auth_mnesia_migration_SUITE.erl deleted file mode 100644 index 7c249b66b..000000000 --- a/apps/emqx_management/test/emqx_auth_mnesia_migration_SUITE.erl +++ /dev/null @@ -1,176 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_auth_mnesia_migration_SUITE). - --compile(export_all). --compile(nowarn_export_all). - --include_lib("eunit/include/eunit.hrl"). - --include_lib("emqx/include/emqx.hrl"). --include_lib("emqx/include/emqx_mqtt.hrl"). --include_lib("emqx_auth_mnesia/include/emqx_auth_mnesia.hrl"). - -matrix() -> - [{ImportAs, Version} || ImportAs <- [clientid, username] - , Version <- ["v4.2.10", "v4.1.5"]]. - -all() -> - [t_import_4_0, t_import_4_1, t_import_4_2]. - -groups() -> - [{username, [], cases()}, {clientid, [], cases()}]. - -cases() -> - [t_import]. - -init_per_suite(Config) -> - emqx_ct_helpers:start_apps([emqx_management, emqx_dashboard, emqx_auth_mnesia]), - application:set_env(ekka, strict_mode, true), - ekka_mnesia:start(), - emqx_mgmt_auth:mnesia(boot), - Config. - -end_per_suite(_Config) -> - emqx_ct_helpers:stop_apps([emqx_modules, emqx_management, emqx_dashboard, emqx_auth_mnesia]), - ekka_mnesia:ensure_stopped(). - -init_per_testcase(_, Config) -> - Config. - -end_per_testcase(_, _Config) -> - {atomic,ok} = mnesia:clear_table(emqx_acl), - {atomic,ok} = mnesia:clear_table(emqx_user), - ok. --ifdef(EMQX_ENTERPRISE). -t_import_4_0(Config) -> - Overrides = emqx_json:encode(#{<<"auth.mnesia.as">> => atom_to_binary(clientid)}), - ?assertMatch(ok, do_import("e4.0.10.json", Config, Overrides)), - timer:sleep(100), - ct:pal("---~p~n", [ets:tab2list(emqx_user)]), - test_import(username, {<<"emqx_username">>, <<"public">>}), - test_import(clientid, {<<"emqx_c">>, <<"public">>}), - - Overrides1 = emqx_json:encode(#{<<"auth.mnesia.as">> => atom_to_binary(username)}), - ?assertMatch(ok, do_import("e4.0.10.json", Config, Overrides1)), - timer:sleep(100), - test_import(username, {<<"emqx_c">>, <<"public">>}), - test_import(username, {<<"emqx_username">>, <<"public">>}). -t_import_4_1(Config) -> - Overrides = emqx_json:encode(#{<<"auth.mnesia.as">> => atom_to_binary(clientid)}), - ?assertMatch(ok, do_import("e4.1.1.json", Config, Overrides)), - timer:sleep(100), - test_import(clientid, {<<"emqx_c">>, <<"public">>}), - test_import(clientid, {<<"emqx_c">>, <<"public">>}), - - Overrides1 = emqx_json:encode(#{<<"auth.mnesia.as">> => atom_to_binary(username)}), - ?assertMatch(ok, do_import("e4.1.1.json", Config, Overrides1)), - timer:sleep(100), - test_import(username, {<<"emqx_c">>, <<"public">>}), - test_import(clientid, {<<"emqx_clientid">>, <<"public">>}). - -t_import_4_2(Config) -> - ?assertMatch(ok, do_import("e4.2.9.json", Config, "{}")), - timer:sleep(100), - test_import(username, {<<"emqx_c">>, <<"public">>}), - test_import(clientid, {<<"emqx_clientid">>, <<"public">>}). - --else. -t_import_4_0(Config) -> - ?assertMatch(ok, do_import("v4.0.11-no-auth.json", Config)), - timer:sleep(100), - ?assertMatch(0, ets:info(emqx_user, size)), - - ?assertMatch({error, unsupported_version, "4.0"}, do_import("v4.0.11.json", Config)), - - ?assertMatch(ok, do_import("v4.0.13.json", Config)), - timer:sleep(100), - test_import(clientid, {<<"client_for_test">>, <<"public">>}), - test_import(username, {<<"user_for_test">>, <<"public">>}). - -t_import_4_1(Config) -> - Overrides = emqx_json:encode(#{<<"auth.mnesia.as">> => atom_to_binary(clientid)}), - ?assertMatch(ok, do_import("v4.1.5.json", Config, Overrides)), - timer:sleep(100), - test_import(clientid, {<<"user_mnesia">>, <<"public">>}), - test_import(clientid, {<<"client_for_test">>, <<"public">>}), - test_import(username, {<<"user_for_test">>, <<"public">>}), - - Overrides1 = emqx_json:encode(#{<<"auth.mnesia.as">> => atom_to_binary(username)}), - ?assertMatch(ok, do_import("v4.1.5.json", Config, Overrides1)), - timer:sleep(100), - test_import(username, {<<"user_mnesia">>, <<"public">>}), - test_import(clientid, {<<"client_for_test">>, <<"public">>}), - test_import(username, {<<"user_for_test">>, <<"public">>}). - -t_import_4_2(Config) -> - ?assertMatch(ok, do_import("v4.2.10-no-auth.json", Config)), - timer:sleep(100), - ?assertMatch(0, ets:info(emqx_user, size)), - - Overrides = emqx_json:encode(#{<<"auth.mnesia.as">> => atom_to_binary(clientid)}), - ?assertMatch({error, unsupported_version, "4.2"}, do_import("v4.2.10.json", Config, Overrides)), - - Overrides1 = emqx_json:encode(#{<<"auth.mnesia.as">> => atom_to_binary(clientid)}), - ?assertMatch(ok, do_import("v4.2.11.json", Config, Overrides1)), - timer:sleep(100), - test_import(clientid, {<<"user_mnesia">>, <<"public">>}), - test_import(clientid, {<<"client_for_test">>, <<"public">>}), - test_import(username, {<<"user_for_test">>, <<"public">>}), - - Overrides2 = emqx_json:encode(#{<<"auth.mnesia.as">> => atom_to_binary(username)}), - ?assertMatch(ok, do_import("v4.2.11.json", Config, Overrides2)), - timer:sleep(100), - test_import(username, {<<"user_mnesia">>, <<"public">>}), - test_import(clientid, {<<"client_for_test">>, <<"public">>}), - test_import(username, {<<"user_for_test">>, <<"public">>}), - - ?assertMatch([#emqx_acl{ - filter = {{Type,<<"emqx_c">>}, <<"Topic/A">>}, - action = pub, - access = allow - }, - #emqx_acl{ - filter = {{Type,<<"emqx_c">>}, <<"Topic/A">>}, - action = sub, - access = allow - }], - lists:sort(ets:tab2list(emqx_acl))). --endif. - -do_import(File, Config) -> - do_import(File, Config, "{}"). - -do_import(File, Config, Overrides) -> - mnesia:clear_table(emqx_acl), - mnesia:clear_table(emqx_user), - Filename = filename:join(proplists:get_value(data_dir, Config), File), - emqx_mgmt_data_backup:import(Filename, Overrides). - -test_import(username, {Username, Password}) -> - [#emqx_user{password = _}] = ets:lookup(emqx_user, {username, Username}), - Req = #{clientid => <<"anyname">>, - username => Username, - password => Password}, - ?assertMatch({stop, #{auth_result := success}}, - emqx_auth_mnesia:check(Req, #{}, #{hash_type => sha256})); -test_import(clientid, {ClientID, Password}) -> - [#emqx_user{password = _}] = ets:lookup(emqx_user, {clientid, ClientID}), - Req = #{clientid => ClientID, - password => Password}, - ?assertMatch({stop, #{auth_result := success}}, - emqx_auth_mnesia:check(Req, #{}, #{hash_type => sha256})). diff --git a/apps/emqx_management/test/emqx_auth_mnesia_migration_SUITE_data/make_data.sh b/apps/emqx_management/test/emqx_auth_mnesia_migration_SUITE_data/make_data.sh deleted file mode 100755 index e31729784..000000000 --- a/apps/emqx_management/test/emqx_auth_mnesia_migration_SUITE_data/make_data.sh +++ /dev/null @@ -1,134 +0,0 @@ -#!/bin/bash -set -eux pipefail -# Helper script for creating data export files - -container() { - version="${1}" - if [ -z ${2+x} ]; then - ee="" - else - ee="-ee" - fi - container="emqx/emqx${ee}:${version}" - docker rm -f emqx || true - docker run "$container" true # Make sure the image is cached locally - docker run --rm -e EMQX_LOADED_PLUGINS="emqx_auth_mnesia emqx_auth_clientid emqx_management" \ - --name emqx -p 8081:8081 "$container" emqx foreground & - sleep 7 -} - -create_acls () { - url="${1}" - curl -f -v "http://localhost:8081/$url" -u admin:public -d@- < "$filename.json" - cat "${filename}.json" -} - - -collect_4_2_no_mnesia_auth () { - container "4.2.10" - - # Add clientid - docker exec emqx emqx_ctl clientid add emqx_clientid emqx_p - - export_data "v4.2.10-no-auth" -} - -collect_4_2 () { - container "4.2.10" - create_acls "api/v4/mqtt_acl" - create_user mqtt_user - - # Add clientid - docker exec emqx emqx_ctl clientid add emqx_clientid emqx_p - - export_data "v4.2.10" -} - - -collect_e4_2 () { - container "4.2.5" "ee" - # Add ACLs: - docker exec emqx emqx_ctl acl add username emqx_c Topic/A pubsub allow - # Create users - docker exec emqx emqx_ctl user add emqx_c emqx_p - - # Add clientid - docker exec emqx emqx_ctl clientid add emqx_clientid emqx_p - - export_data "e4.2.5" -} - -collect_e4_1 () { - container "4.1.1" "ee" - # Add ACLs: - create_acls "api/v4/emqx_acl" - # Create users - create_user "auth_user" - - # Add clientid - docker exec emqx emqx_ctl clientid add emqx_clientid emqx_p - - export_data "e4.1.1" -} - -collect_4_1 () { - container "v4.1.5" - create_acls "api/v4/emqx_acl" - create_user auth_user - - # Add clientid - docker exec emqx emqx_ctl clientid add emqx_clientid emqx_p - - export_data "v4.1.5" -} - -collect_4_0 () { - container "v4.0.11" - - # Add clientid - docker exec emqx emqx_ctl clientid add emqx_clientid emqx_p - - export_data "v4.0.11" -} - -collect_4_0 -collect_4_1 -collect_4_2 -collect_4_2_no_mnesia_auth -collect_e4_2 -collect_e4_1 - -docker kill emqx diff --git a/apps/emqx_management/test/emqx_auth_mnesia_migration_SUITE_data/v4.0.11-no-auth.json b/apps/emqx_management/test/emqx_auth_mnesia_migration_SUITE_data/v4.0.11-no-auth.json deleted file mode 100644 index 8dad197dd..000000000 --- a/apps/emqx_management/test/emqx_auth_mnesia_migration_SUITE_data/v4.0.11-no-auth.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "version": "4.0", - "users": [], - "schemas": [], - "rules": [], - "resources": [], - "date": "2021-04-10 11:45:26", - "blacklist": [], - "auth_username": [], - "auth_mnesia": [], - "auth_clientid": [], - "apps": [ - { - "status": true, - "secret": "public", - "name": "Default", - "id": "admin", - "expired": "undefined", - "desc": "Application user" - } - ], - "acl_mnesia": [] -} diff --git a/apps/emqx_management/test/emqx_auth_mnesia_migration_SUITE_data/v4.0.11.json b/apps/emqx_management/test/emqx_auth_mnesia_migration_SUITE_data/v4.0.11.json deleted file mode 100644 index a701d0944..000000000 --- a/apps/emqx_management/test/emqx_auth_mnesia_migration_SUITE_data/v4.0.11.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "version": "4.0", - "users": [], - "schemas": [], - "rules": [], - "resources": [], - "date": "2021-04-10 11:45:26", - "blacklist": [], - "auth_username": [], - "auth_mnesia": [], - "auth_clientid": [ - { - "password": "9Sv2tzJlNDlmNWZhYWQ5Yzc4MWUwNmFhZWI4NjFlMDM2OWEzYmE1OTkxOTBhOGQ4N2Y3MzExY2ZiZmIxNTFkMTdkZmY=", - "clientid": "emqx_clientid" - } - ], - "apps": [ - { - "status": true, - "secret": "public", - "name": "Default", - "id": "admin", - "expired": "undefined", - "desc": "Application user" - } - ], - "acl_mnesia": [] -} diff --git a/apps/emqx_management/test/emqx_auth_mnesia_migration_SUITE_data/v4.0.13.json b/apps/emqx_management/test/emqx_auth_mnesia_migration_SUITE_data/v4.0.13.json deleted file mode 100644 index 9c602a0e3..000000000 --- a/apps/emqx_management/test/emqx_auth_mnesia_migration_SUITE_data/v4.0.13.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "version":"4.0.13", - "users":[ - { - "username":"admin", - "tags":"administrator", - "password":"p6C65OF0BQhvPmCziM2yRa8JN5o=" - } - ], - "schemas":[], - "rules":[], - "resources":[], - "date":"2021-04-16 11:20:00", - "blacklist":[], - "auth_username":[ - { - "username":"user_for_test", - "password":"ARLrzTRhZTI1MzgxNjdjMDU5ODFhZDU3ZTdmNzJiOWM5MWUwMTFkNDk4OGUyZWUyYmU0ZTE2ZTg2OWNhMGQyYWQ5ZmU=" - } - ], - "auth_clientid":[ - { - "password":"JBgSnzIxOWNiMDU1ZWFiNDAwMjVhOTQzZThlZjkxN2JlZWE4MGE4YzlmM2I5MjQ4OGI1NjllY2Q4NGQ4NjhjYzQ1NDM=", - "clientid":"client_for_test" - } - ], - "apps":[ - { - "status":true, - "secret":"public", - "name":"Default", - "id":"admin", - "expired":"undefined", - "desc":"Application user" - } - ] -} \ No newline at end of file diff --git a/apps/emqx_management/test/emqx_auth_mnesia_migration_SUITE_data/v4.1.5.json b/apps/emqx_management/test/emqx_auth_mnesia_migration_SUITE_data/v4.1.5.json deleted file mode 100644 index 3d3b9aa7e..000000000 --- a/apps/emqx_management/test/emqx_auth_mnesia_migration_SUITE_data/v4.1.5.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "version": "4.1", - "users": [ - { - "username": "admin", - "tags": "administrator", - "password": "R0TpDmJtE/d5rIXAm6YY61RI0mg=" - } - ], - "schemas": [], - "rules": [], - "resources": [], - "date": "2021-04-07 14:28:58", - "blacklist": [], - "auth_username": [ - { - "username":"user_for_test", - "password": "ARLrzTRhZTI1MzgxNjdjMDU5ODFhZDU3ZTdmNzJiOWM5MWUwMTFkNDk4OGUyZWUyYmU0ZTE2ZTg2OWNhMGQyYWQ5ZmU=" - } - ], - "auth_mnesia": [ - { - "password": "ARLrzTRhZTI1MzgxNjdjMDU5ODFhZDU3ZTdmNzJiOWM5MWUwMTFkNDk4OGUyZWUyYmU0ZTE2ZTg2OWNhMGQyYWQ5ZmU=", - "login": "user_mnesia", - "is_superuser": true - } - ], - "auth_clientid": [ - { - "password": "ARLrzTRhZTI1MzgxNjdjMDU5ODFhZDU3ZTdmNzJiOWM5MWUwMTFkNDk4OGUyZWUyYmU0ZTE2ZTg2OWNhMGQyYWQ5ZmU=", - "clientid": "client_for_test" - } - ], - "apps": [ - { - "status": true, - "secret": "public", - "name": "Default", - "id": "admin", - "expired": "undefined", - "desc": "Application user" - } - ], - "acl_mnesia": [ - { - "topic": "Topic/A", - "login": "emqx_c", - "allow": true, - "action": "sub" - }, - { - "topic": "Topic/A", - "login": "emqx_c", - "allow": true, - "action": "pub" - } - ] -} diff --git a/apps/emqx_management/test/emqx_auth_mnesia_migration_SUITE_data/v4.2.10-no-auth.json b/apps/emqx_management/test/emqx_auth_mnesia_migration_SUITE_data/v4.2.10-no-auth.json deleted file mode 100644 index 446a46f07..000000000 --- a/apps/emqx_management/test/emqx_auth_mnesia_migration_SUITE_data/v4.2.10-no-auth.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "version": "4.2", - "date": "2021-04-12 10:41:10", - "rules": [], - "resources": [], - "blacklist": [], - "apps": [ - { - "id": "admin", - "secret": "public", - "name": "Default", - "desc": "Application user", - "status": true, - "expired": "undefined" - } - ], - "users": [ - { - "username": "admin", - "password": "e5M8oWEwQVqjdqceQIthC+3cPoY=", - "tags": "administrator" - } - ], - "auth_clientid": [], - "auth_username": [], - "auth_mnesia": [], - "acl_mnesia": [], - "schemas": [] -} diff --git a/apps/emqx_management/test/emqx_auth_mnesia_migration_SUITE_data/v4.2.10.json b/apps/emqx_management/test/emqx_auth_mnesia_migration_SUITE_data/v4.2.10.json deleted file mode 100644 index 1ccc6ce9d..000000000 --- a/apps/emqx_management/test/emqx_auth_mnesia_migration_SUITE_data/v4.2.10.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "version": "4.2", - "date": "2021-04-12 10:40:58", - "rules": [], - "resources": [], - "blacklist": [], - "apps": [ - { - "id": "admin", - "secret": "public", - "name": "Default", - "desc": "Application user", - "status": true, - "expired": "undefined" - } - ], - "users": [ - { - "username": "admin", - "password": "8Vd7+gVg2J3nE1Xjyxqd59sA5mo=", - "tags": "administrator" - } - ], - "auth_clientid": [ - { - "clientid": "emqx_clientid", - "password": "UNb0e2RhNDc3NWIyNjg5Yjg4ZDExOTVhNWFkY2MzNGFmNzY2OTNmNmRlYzE4Y2ZiZjRjNzIyMWZlZTljZmEyZDE5Yzc=" - } - ], - "auth_username": [], - "auth_mnesia": [ - { - "login": "emqx_c", - "password": "ceb5e917f7930ae8f0dc3ceb496a428f7e644736eebca36a2b8f6bbac756171a", - "is_superuser": true - } - ], - "acl_mnesia": [ - { - "login": "emqx_c", - "topic": "Topic/A", - "action": "sub", - "allow": true - }, - { - "login": "emqx_c", - "topic": "Topic/A", - "action": "pub", - "allow": true - } - ], - "schemas": [] -} diff --git a/apps/emqx_management/test/emqx_auth_mnesia_migration_SUITE_data/v4.2.11.json b/apps/emqx_management/test/emqx_auth_mnesia_migration_SUITE_data/v4.2.11.json deleted file mode 100644 index 52a91ac68..000000000 --- a/apps/emqx_management/test/emqx_auth_mnesia_migration_SUITE_data/v4.2.11.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "version": "4.2.11", - "date": "2021-04-12 10:40:58", - "rules": [], - "resources": [], - "blacklist": [], - "apps": [ - { - "id": "admin", - "secret": "public", - "name": "Default", - "desc": "Application user", - "status": true, - "expired": "undefined" - } - ], - "users": [ - { - "username": "test", - "password": "8Vd7+gVg2J3nE1Xjyxqd59sA5mo=", - "tags": "administrator" - } - ], - "auth_clientid": [ - { - "clientid": "client_for_test", - "password": "ARLrzTRhZTI1MzgxNjdjMDU5ODFhZDU3ZTdmNzJiOWM5MWUwMTFkNDk4OGUyZWUyYmU0ZTE2ZTg2OWNhMGQyYWQ5ZmU=" - } - ], - "auth_username": [ - { - "username": "user_for_test", - "password": "ARLrzTRhZTI1MzgxNjdjMDU5ODFhZDU3ZTdmNzJiOWM5MWUwMTFkNDk4OGUyZWUyYmU0ZTE2ZTg2OWNhMGQyYWQ5ZmU=" - } - ], - "auth_mnesia": [ - { - "login": "user_mnesia", - "password": "ARLrzTRhZTI1MzgxNjdjMDU5ODFhZDU3ZTdmNzJiOWM5MWUwMTFkNDk4OGUyZWUyYmU0ZTE2ZTg2OWNhMGQyYWQ5ZmU=", - "is_superuser": true - } - ], - "acl_mnesia": [ - { - "login": "emqx_c", - "topic": "Topic/A", - "action": "sub", - "allow": true - }, - { - "login": "emqx_c", - "topic": "Topic/A", - "action": "pub", - "allow": true - } - ], - "schemas": [] -} diff --git a/rebar.config b/rebar.config index 66d24fe91..1d871d944 100644 --- a/rebar.config +++ b/rebar.config @@ -35,7 +35,6 @@ {deps, [ {gpb, "4.11.2"} %% gpb only used to build, but not for release, pin it here to avoid fetching a wrong version due to rebar plugins scattered in all the deps , {ehttpc, {git, "https://github.com/emqx/ehttpc", {tag, "0.1.6"}}} - , {eredis_cluster, {git, "https://github.com/emqx/eredis_cluster", {tag, "0.6.7"}}} , {gproc, {git, "https://github.com/uwiger/gproc", {tag, "0.8.0"}}} , {jiffy, {git, "https://github.com/emqx/jiffy", {tag, "1.0.5"}}} , {cowboy, {git, "https://github.com/emqx/cowboy", {tag, "2.8.2"}}} From 503088186e591df65bbb2dc4a94ef7876af0d6f8 Mon Sep 17 00:00:00 2001 From: Rory Z Date: Fri, 25 Jun 2021 10:58:34 +0800 Subject: [PATCH 030/174] chore(CI): update ci env --- .github/workflows/run_test_cases.yaml | 43 --------------------------- 1 file changed, 43 deletions(-) diff --git a/.github/workflows/run_test_cases.yaml b/.github/workflows/run_test_cases.yaml index 3fbe88c40..812058111 100644 --- a/.github/workflows/run_test_cases.yaml +++ b/.github/workflows/run_test_cases.yaml @@ -56,64 +56,21 @@ jobs: - name: docker compose up if: env.EDITION == 'opensource' env: - MYSQL_TAG: 8 - REDIS_TAG: 6 - MONGO_TAG: 4 - PGSQL_TAG: 13 - LDAP_TAG: 2.4.50 GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | docker-compose \ -f .ci/docker-compose-file/docker-compose.yaml \ - -f .ci/docker-compose-file/docker-compose-ldap-tcp.yaml \ - -f .ci/docker-compose-file/docker-compose-mongo-tcp.yaml \ - -f .ci/docker-compose-file/docker-compose-mysql-tcp.yaml \ - -f .ci/docker-compose-file/docker-compose-pgsql-tcp.yaml \ - -f .ci/docker-compose-file/docker-compose-redis-single-tcp.yaml \ up -d --build - name: docker compose up if: env.EDITION == 'enterprise' env: - MYSQL_TAG: 8 - REDIS_TAG: 6 - MONGO_TAG: 4 - PGSQL_TAG: 13 - LDAP_TAG: 2.4.50 - OPENTSDB_TAG: latest - INFLUXDB_TAG: 1.7.6 - DYNAMODB_TAG: 1.11.477 - TIMESCALE_TAG: latest-pg11 - CASSANDRA_TAG: 3.11.6 - RABBITMQ_TAG: 3.7 - KAFKA_TAG: 2.5.0 - PULSAR_TAG: 2.3.2 GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} timeout-minutes: 20 run: | docker-compose \ -f .ci/docker-compose-file/docker-compose.yaml \ - -f .ci/docker-compose-file/docker-compose-ldap-tcp.yaml \ - -f .ci/docker-compose-file/docker-compose-mongo-tcp.yaml \ - -f .ci/docker-compose-file/docker-compose-mysql-tcp.yaml \ - -f .ci/docker-compose-file/docker-compose-pgsql-tcp.yaml \ - -f .ci/docker-compose-file/docker-compose-redis-single-tcp.yaml \ -f .ci/docker-compose-file/docker-compose-enterprise.yaml \ - -f .ci/docker-compose-file/docker-compose-enterprise-cassandra-tcp.yaml \ - -f .ci/docker-compose-file/docker-compose-enterprise-dynamodb-tcp.yaml \ - -f .ci/docker-compose-file/docker-compose-enterprise-influxdb-tcp.yaml \ - -f .ci/docker-compose-file/docker-compose-enterprise-kafka-tcp.yaml \ - -f .ci/docker-compose-file/docker-compose-enterprise-opentsdb-tcp.yaml \ - -f .ci/docker-compose-file/docker-compose-enterprise-pulsar-tcp.yaml \ - -f .ci/docker-compose-file/docker-compose-enterprise-rabbit-tcp.yaml \ - -f .ci/docker-compose-file/docker-compose-enterprise-timescale-tcp.yaml \ - -f .ci/docker-compose-file/docker-compose-enterprise-mysql-client.yaml \ - -f .ci/docker-compose-file/docker-compose-enterprise-pgsql-and-timescale-client.yaml \ up -d --build - docker exec -i erlang bash -c "echo \"https://ci%40emqx.io:${{ secrets.CI_GIT_TOKEN }}@github.com\" > /root/.git-credentials && git config --global credential.helper store" - while [ $(docker ps -a --filter name=client --filter exited=0 | wc -l) \ - != $(docker ps -a --filter name=client | wc -l) ]; do - sleep 5 - done - name: run eunit run: | docker exec -i erlang bash -c "make eunit" From 55613593f0e30064cff2e1e4386b050e34193649 Mon Sep 17 00:00:00 2001 From: Rory Z Date: Fri, 25 Jun 2021 11:35:26 +0800 Subject: [PATCH 031/174] chore: delete import and export feature --- apps/emqx_connector/rebar.config | 2 +- ...x_bridge_mqtt_data_export_import_SUITE.erl | 184 ----------------- .../409.json | 68 ------ .../415.json | 70 ------- .../420.json | 70 ------- .../430.json | 76 ------- .../ee4010.json | 68 ------ .../ee410.json | 70 ------- .../ee411.json | 70 ------- .../ee420.json | 119 ----------- .../ee425.json | 123 ----------- .../ee430.json | 123 ----------- .../test/emqx_mgmt_api_SUITE.erl | 33 --- .../emqx_webhook_data_export_import_SUITE.erl | 194 ------------------ .../409.json | 43 ---- .../415.json | 42 ---- .../422.json | 43 ---- .../423.json | 44 ---- .../425.json | 47 ----- .../430.json | 52 ----- .../ee4010.json | 43 ---- .../ee410.json | 43 ---- .../ee411.json | 43 ---- .../ee420.json | 92 --------- .../ee425.json | 102 --------- .../ee430.json | 98 --------- 26 files changed, 1 insertion(+), 1961 deletions(-) delete mode 100644 apps/emqx_management/test/emqx_bridge_mqtt_data_export_import_SUITE.erl delete mode 100644 apps/emqx_management/test/emqx_bridge_mqtt_data_export_import_SUITE_data/409.json delete mode 100644 apps/emqx_management/test/emqx_bridge_mqtt_data_export_import_SUITE_data/415.json delete mode 100644 apps/emqx_management/test/emqx_bridge_mqtt_data_export_import_SUITE_data/420.json delete mode 100644 apps/emqx_management/test/emqx_bridge_mqtt_data_export_import_SUITE_data/430.json delete mode 100644 apps/emqx_management/test/emqx_bridge_mqtt_data_export_import_SUITE_data/ee4010.json delete mode 100644 apps/emqx_management/test/emqx_bridge_mqtt_data_export_import_SUITE_data/ee410.json delete mode 100644 apps/emqx_management/test/emqx_bridge_mqtt_data_export_import_SUITE_data/ee411.json delete mode 100644 apps/emqx_management/test/emqx_bridge_mqtt_data_export_import_SUITE_data/ee420.json delete mode 100644 apps/emqx_management/test/emqx_bridge_mqtt_data_export_import_SUITE_data/ee425.json delete mode 100644 apps/emqx_management/test/emqx_bridge_mqtt_data_export_import_SUITE_data/ee430.json delete mode 100644 apps/emqx_management/test/emqx_webhook_data_export_import_SUITE.erl delete mode 100644 apps/emqx_management/test/emqx_webhook_data_export_import_SUITE_data/409.json delete mode 100644 apps/emqx_management/test/emqx_webhook_data_export_import_SUITE_data/415.json delete mode 100644 apps/emqx_management/test/emqx_webhook_data_export_import_SUITE_data/422.json delete mode 100644 apps/emqx_management/test/emqx_webhook_data_export_import_SUITE_data/423.json delete mode 100644 apps/emqx_management/test/emqx_webhook_data_export_import_SUITE_data/425.json delete mode 100644 apps/emqx_management/test/emqx_webhook_data_export_import_SUITE_data/430.json delete mode 100644 apps/emqx_management/test/emqx_webhook_data_export_import_SUITE_data/ee4010.json delete mode 100644 apps/emqx_management/test/emqx_webhook_data_export_import_SUITE_data/ee410.json delete mode 100644 apps/emqx_management/test/emqx_webhook_data_export_import_SUITE_data/ee411.json delete mode 100644 apps/emqx_management/test/emqx_webhook_data_export_import_SUITE_data/ee420.json delete mode 100644 apps/emqx_management/test/emqx_webhook_data_export_import_SUITE_data/ee425.json delete mode 100644 apps/emqx_management/test/emqx_webhook_data_export_import_SUITE_data/ee430.json diff --git a/apps/emqx_connector/rebar.config b/apps/emqx_connector/rebar.config index 633b427cf..b12bd3edb 100644 --- a/apps/emqx_connector/rebar.config +++ b/apps/emqx_connector/rebar.config @@ -11,7 +11,7 @@ %% NOTE: mind poolboy version when updating mongodb-erlang version {mongodb, {git,"https://github.com/emqx/mongodb-erlang", {tag, "v3.0.7"}}}, %% NOTE: mind poolboy version when updating eredis_cluster version - {eredis_cluster, {git, "https://github.com/emqx/eredis_cluster", {tag, "0.6.6"}}}, + {eredis_cluster, {git, "https://github.com/emqx/eredis_cluster", {tag, "0.6.7"}}}, %% mongodb-erlang uses a special fork https://github.com/comtihon/poolboy.git %% (which has overflow_ttl feature added). %% However, it references `{branch, "master}` (commit 9c06a9a on 2021-04-07). diff --git a/apps/emqx_management/test/emqx_bridge_mqtt_data_export_import_SUITE.erl b/apps/emqx_management/test/emqx_bridge_mqtt_data_export_import_SUITE.erl deleted file mode 100644 index 5667cb73f..000000000 --- a/apps/emqx_management/test/emqx_bridge_mqtt_data_export_import_SUITE.erl +++ /dev/null @@ -1,184 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_bridge_mqtt_data_export_import_SUITE). --include_lib("eunit/include/eunit.hrl"). --include_lib("emqx_rule_engine/include/rule_engine.hrl"). --compile([export_all, nowarn_export_all]). - -% -define(EMQX_ENTERPRISE, true). - -%%-------------------------------------------------------------------- -%% Setups -%%-------------------------------------------------------------------- -all() -> - emqx_ct:all(?MODULE). - -init_per_suite(Cfg) -> - application:load(emqx_modules), - application:load(emqx_bridge_mqtt), - emqx_ct_helpers:start_apps([emqx_rule_engine, emqx_management]), - Cfg. - -end_per_suite(Cfg) -> - emqx_ct_helpers:stop_apps([emqx_management, emqx_rule_engine]), - Cfg. - -get_data_path() -> - emqx_ct_helpers:deps_path(emqx_management, "test/emqx_bridge_mqtt_data_export_import_SUITE_data/"). - -import(FilePath, Version) -> - ok = emqx_mgmt_data_backup:import(get_data_path() ++ "/" ++ FilePath, <<"{}">>), - timer:sleep(500), - lists:foreach(fun(#resource{id = Id, config = Config} = _Resource) -> - case Id of - <<"bridge">> -> - test_utils:resource_is_alive(Id), - handle_config(Config, Version, bridge); - <<"rpc">> -> - test_utils:resource_is_alive(Id), - handle_config(Config, Version, rpc); - _ -> ok - end - end, emqx_rule_registry:get_resources()). - -%%-------------------------------------------------------------------- -%% Cases -%%-------------------------------------------------------------------- - --ifndef(EMQX_ENTERPRISE). - -t_import420(_) -> - import("420.json", 420), - {ok, _} = emqx_mgmt_data_backup:export(), - remove_resources(). - -t_import430(_) -> - import("430.json", 430), - {ok, _} = emqx_mgmt_data_backup:export(), - remove_resources(). - -t_import409(_) -> - import("409.json", 409), - {ok, _} = emqx_mgmt_data_backup:export(), - remove_resources(). - -t_import415(_) -> - import("415.json", 415), - {ok, _} = emqx_mgmt_data_backup:export(), - remove_resources(). - - -handle_config(Config, 420, bridge) -> - ?assertEqual(false, maps:get(<<"ssl">>, Config)); - -handle_config(Config, 430, bridge) -> - ?assertEqual(false, maps:get(<<"ssl">>, Config)); - -handle_config(Config, 420, rpc) -> - handle_config(Config, 430, rpc); - -handle_config(Config, 409, rpc) -> - handle_config(Config, 420, rpc); - -handle_config(Config, 415, rpc) -> - handle_config(Config, 420, rpc); - -handle_config(Config, 409, bridge) -> - handle_config(Config, 420, bridge); - -handle_config(Config, 415, bridge) -> - handle_config(Config, 420, bridge); - -handle_config(Config, 430, rpc) -> - ?assertEqual(<<"test@127.0.0.1">>, maps:get(<<"address">>, Config)), - ?assertEqual(32, maps:get(<<"batch_size">>, Config)), - ?assertEqual(<<"off">>, maps:get(<<"disk_cache">>, Config)), - ?assertEqual(<<"bridge/emqx/${node}/">>, maps:get(<<"mountpoint">>, Config)), - ?assertEqual(<<"30s">>, maps:get(<<"reconnect_interval">>, Config)), - ?assertEqual(8, maps:get(<<"pool_size">>, Config)); - -handle_config(_, _, _) -> ok. - --endif. - --ifdef(EMQX_ENTERPRISE). - -t_importee4010(_) -> - import("ee4010.json", ee4010), - {ok, _} = emqx_mgmt_data_backup:export(), - remove_resources(). - -t_importee410(_) -> - import("ee410.json", ee410), - {ok, _} = emqx_mgmt_data_backup:export(), - remove_resources(). - -t_importee411(_) -> - import("ee411.json", ee411), - {ok, _} = emqx_mgmt_data_backup:export(), - remove_resources(). - -t_importee420(_) -> - import("ee420.json", ee420), - {ok, _} = emqx_mgmt_data_backup:export(), - remove_resources(). - -t_importee425(_) -> - import("ee425.json", ee425), - {ok, _} = emqx_mgmt_data_backup:export(), - remove_resources(). - -t_importee430(_) -> - import("ee430.json", ee430), - {ok, _} = emqx_mgmt_data_backup:export(), - remove_resources(). - -%%-------------------------------------------------------------------- -%% handle_config -%%-------------------------------------------------------------------- - -handle_config(Config, ee4010, Id) -> - handle_config(Config, ee430, Id); - -handle_config(Config, ee410, Id) -> - handle_config(Config, ee430, Id); - -handle_config(Config, ee411, Id) -> - handle_config(Config, ee430, Id); - -handle_config(Config, ee420, Id) -> - handle_config(Config, ee430, Id); - -handle_config(Config, ee425, Id) -> - handle_config(Config, ee430, Id); - -handle_config(Config, ee430, bridge) -> - ?assertEqual(false, maps:get(<<"ssl">>, Config)); - -handle_config(Config, ee430, rpc) -> - ?assertEqual(<<"off">>, maps:get(<<"disk_cache">>, Config)); - -handle_config(Config, ee435, Id) -> - handle_config(Config, ee430, Id). --endif. - -remove_resources() -> - timer:sleep(500), - lists:foreach(fun(#resource{id = Id}) -> - emqx_rule_engine:delete_resource(Id) - end, emqx_rule_registry:get_resources()), - timer:sleep(500). diff --git a/apps/emqx_management/test/emqx_bridge_mqtt_data_export_import_SUITE_data/409.json b/apps/emqx_management/test/emqx_bridge_mqtt_data_export_import_SUITE_data/409.json deleted file mode 100644 index cb50a31af..000000000 --- a/apps/emqx_management/test/emqx_bridge_mqtt_data_export_import_SUITE_data/409.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "version": "4.0", - "users": [ - { - "username": "admin", - "tags": "administrator", - "password": "m/GtjNgri9GILklefVJH8BeTnNE=" - } - ], - "schemas": [], - "rules": [], - "resources": [ - { - "type": "bridge_mqtt", - "id": "bridge", - "description": "bridge", - "created_at": null, - "config": { - "username": "user", - "ssl": "off", - "retry_interval": "20s", - "reconnect_interval": "30s", - "proto_ver": "mqttv4", - "password": "passwd", - "mountpoint": "bridge/emqx/${node}/", - "keyfile": "etc/certs/client-key.pem", - "keepalive": "60s", - "disk_cache": "off", - "clientid": "bridge_aws", - "ciphers": "ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384", - "certfile": "etc/certs/client-cert.pem", - "cacertfile": "etc/certs/cacert.pem", - "bridge_mode": true, - "address": "127.0.0.1:1883" - } - }, - { - "type": "bridge_rpc", - "id": "rpc", - "description": "rpc", - "created_at": null, - "config": { - "reconnect_interval": "30s", - "pool_size": 8, - "mountpoint": "bridge/emqx/${node}/", - "disk_cache": "off", - "batch_size": 32, - "address": "test@127.0.0.1" - } - } - ], - "date": "2021-03-30 13:38:39", - "blacklist": [], - "auth_username": [], - "auth_mnesia": [], - "auth_clientid": [], - "apps": [ - { - "status": true, - "secret": "public", - "name": "Default", - "id": "admin", - "expired": "undefined", - "desc": "Application user" - } - ], - "acl_mnesia": [] -} \ No newline at end of file diff --git a/apps/emqx_management/test/emqx_bridge_mqtt_data_export_import_SUITE_data/415.json b/apps/emqx_management/test/emqx_bridge_mqtt_data_export_import_SUITE_data/415.json deleted file mode 100644 index 176ac1f71..000000000 --- a/apps/emqx_management/test/emqx_bridge_mqtt_data_export_import_SUITE_data/415.json +++ /dev/null @@ -1,70 +0,0 @@ -{ - "version": "4.1", - "users": [ - { - "username": "admin", - "tags": "administrator", - "password": "3W+nHOkCZLFspENkIvHKzCbHxHI=" - } - ], - "schemas": [], - "rules": [], - "resources": [ - { - "type": "bridge_rpc", - "id": "rpc", - "description": "rpc", - "created_at": null, - "config": { - "reconnect_interval": "30s", - "pool_size": 8, - "mountpoint": "bridge/emqx/${node}/", - "disk_cache": "off", - "batch_size": 32, - "address": "test@127.0.0.1" - } - }, - { - "type": "bridge_mqtt", - "id": "bridge", - "description": "bridge", - "created_at": null, - "config": { - "username": "user", - "ssl": "off", - "retry_interval": "20s", - "reconnect_interval": "30s", - "proto_ver": "mqttv4", - "pool_size": 8, - "password": "passwd", - "mountpoint": "bridge/emqx/${node}/", - "keyfile": "etc/certs/client-key.pem", - "keepalive": "60s", - "disk_cache": "off", - "clientid": "client", - "ciphers": "ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384,ECDHE-ECDSA-AES256-SHA384,ECDHE-RSA-AES256-SHA384,ECDHE-ECDSA-DES-CBC3-SHA,ECDH-ECDSA-AES256-GCM-SHA384,ECDH-RSA-AES256-GCM-SHA384,ECDH-ECDSA-AES256-SHA384,ECDH-RSA-AES256-SHA384,DHE-DSS-AES256-GCM-SHA384,DHE-DSS-AES256-SHA256,AES256-GCM-SHA384,AES256-SHA256,ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-RSA-AES128-GCM-SHA256,ECDHE-ECDSA-AES128-SHA256,ECDHE-RSA-AES128-SHA256,ECDH-ECDSA-AES128-GCM-SHA256,ECDH-RSA-AES128-GCM-SHA256,ECDH-ECDSA-AES128-SHA256,ECDH-RSA-AES128-SHA256,DHE-DSS-AES128-GCM-SHA256,DHE-DSS-AES128-SHA256,AES128-GCM-SHA256,AES128-SHA256,ECDHE-ECDSA-AES256-SHA,ECDHE-RSA-AES256-SHA,DHE-DSS-AES256-SHA,ECDH-ECDSA-AES256-SHA,ECDH-RSA-AES256-SHA,AES256-SHA,ECDHE-ECDSA-AES128-SHA,ECDHE-RSA-AES128-SHA,DHE-DSS-AES128-SHA,ECDH-ECDSA-AES128-SHA,ECDH-RSA-AES128-SHA,AES128-SHA", - "certfile": "etc/certs/client-cert.pem", - "cacertfile": "etc/certs/cacert.pem", - "bridge_mode": true, - "append": true, - "address": "127.0.0.1:1883" - } - } - ], - "date": "2021-03-30 13:52:53", - "blacklist": [], - "auth_username": [], - "auth_mnesia": [], - "auth_clientid": [], - "apps": [ - { - "status": true, - "secret": "public", - "name": "Default", - "id": "admin", - "expired": "undefined", - "desc": "Application user" - } - ], - "acl_mnesia": [] -} \ No newline at end of file diff --git a/apps/emqx_management/test/emqx_bridge_mqtt_data_export_import_SUITE_data/420.json b/apps/emqx_management/test/emqx_bridge_mqtt_data_export_import_SUITE_data/420.json deleted file mode 100644 index 9922e459f..000000000 --- a/apps/emqx_management/test/emqx_bridge_mqtt_data_export_import_SUITE_data/420.json +++ /dev/null @@ -1,70 +0,0 @@ -{ - "version": "4.2", - "date": "2021-03-23 23:22:30", - "rules": [], - "resources": [ - { - "id": "rpc", - "type": "bridge_rpc", - "config": { - "address": "test@127.0.0.1", - "batch_size": 32, - "disk_cache": "off", - "mountpoint": "bridge/emqx/${node}/", - "pool_size": 8, - "reconnect_interval": "30s" - }, - "created_at": null, - "description": "rpc" - }, - { - "id": "bridge", - "type": "bridge_mqtt", - "config": { - "address": "127.0.0.1:1883", - "append": true, - "bridge_mode": false, - "cacertfile": "etc/certs/cacert.pem", - "certfile": "etc/certs/client-cert.pem", - "ciphers": "ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384,ECDHE-ECDSA-AES256-SHA384,ECDHE-RSA-AES256-SHA384,ECDHE-ECDSA-DES-CBC3-SHA,ECDH-ECDSA-AES256-GCM-SHA384,ECDH-RSA-AES256-GCM-SHA384,ECDH-ECDSA-AES256-SHA384,ECDH-RSA-AES256-SHA384,DHE-DSS-AES256-GCM-SHA384,DHE-DSS-AES256-SHA256,AES256-GCM-SHA384,AES256-SHA256,ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-RSA-AES128-GCM-SHA256,ECDHE-ECDSA-AES128-SHA256,ECDHE-RSA-AES128-SHA256,ECDH-ECDSA-AES128-GCM-SHA256,ECDH-RSA-AES128-GCM-SHA256,ECDH-ECDSA-AES128-SHA256,ECDH-RSA-AES128-SHA256,DHE-DSS-AES128-GCM-SHA256,DHE-DSS-AES128-SHA256,AES128-GCM-SHA256,AES128-SHA256,ECDHE-ECDSA-AES256-SHA,ECDHE-RSA-AES256-SHA,DHE-DSS-AES256-SHA,ECDH-ECDSA-AES256-SHA,ECDH-RSA-AES256-SHA,AES256-SHA,ECDHE-ECDSA-AES128-SHA,ECDHE-RSA-AES128-SHA,DHE-DSS-AES128-SHA,ECDH-ECDSA-AES128-SHA,ECDH-RSA-AES128-SHA,AES128-SHA", - "clientid": "client", - "disk_cache": "off", - "keepalive": "60s", - "keyfile": "etc/certs/client-key.pem", - "mountpoint": "bridge/emqx/${node}/", - "password": "", - "pool_size": 8, - "proto_ver": "mqttv4", - "reconnect_interval": "30s", - "retry_interval": "20s", - "ssl": "off", - "username": "" - }, - "created_at": null, - "description": "bridge" - } - ], - "blacklist": [], - "apps": [ - { - "id": "admin", - "secret": "public", - "name": "Default", - "desc": "Application user", - "status": true, - "expired": "undefined" - } - ], - "users": [ - { - "username": "admin", - "password": "mOTRLWt85F7GW+CSiBuRUZiRANw=", - "tags": "administrator" - } - ], - "auth_clientid": [], - "auth_username": [], - "auth_mnesia": [], - "acl_mnesia": [], - "schemas": [] -} \ No newline at end of file diff --git a/apps/emqx_management/test/emqx_bridge_mqtt_data_export_import_SUITE_data/430.json b/apps/emqx_management/test/emqx_bridge_mqtt_data_export_import_SUITE_data/430.json deleted file mode 100644 index 4f535bfb7..000000000 --- a/apps/emqx_management/test/emqx_bridge_mqtt_data_export_import_SUITE_data/430.json +++ /dev/null @@ -1,76 +0,0 @@ -{ - "version": "4.3", - "rules": [], - "resources": [ - { - "id": "brigde", - "type": "bridge_mqtt", - "config": { - "address": "127.0.0.1:1883", - "append": true, - "bridge_mode": false, - "cacertfile": { - "filename": "etc/certs/cacert.pem", - "file": "" - }, - "certfile": { - "filename": "etc/certs/client-cert.pem", - "file": "" - }, - "ciphers": "ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384,ECDHE-ECDSA-AES256-SHA384,ECDHE-RSA-AES256-SHA384,ECDHE-ECDSA-DES-CBC3-SHA,ECDH-ECDSA-AES256-GCM-SHA384,ECDH-RSA-AES256-GCM-SHA384,ECDH-ECDSA-AES256-SHA384,ECDH-RSA-AES256-SHA384,DHE-DSS-AES256-GCM-SHA384,DHE-DSS-AES256-SHA256,AES256-GCM-SHA384,AES256-SHA256,ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-RSA-AES128-GCM-SHA256,ECDHE-ECDSA-AES128-SHA256,ECDHE-RSA-AES128-SHA256,ECDH-ECDSA-AES128-GCM-SHA256,ECDH-RSA-AES128-GCM-SHA256,ECDH-ECDSA-AES128-SHA256,ECDH-RSA-AES128-SHA256,DHE-DSS-AES128-GCM-SHA256,DHE-DSS-AES128-SHA256,AES128-GCM-SHA256,AES128-SHA256,ECDHE-ECDSA-AES256-SHA,ECDHE-RSA-AES256-SHA,DHE-DSS-AES256-SHA,ECDH-ECDSA-AES256-SHA,ECDH-RSA-AES256-SHA,AES256-SHA,ECDHE-ECDSA-AES128-SHA,ECDHE-RSA-AES128-SHA,DHE-DSS-AES128-SHA,ECDH-ECDSA-AES128-SHA,ECDH-RSA-AES128-SHA,AES128-SHA", - "clientid": "client", - "disk_cache": "off", - "keepalive": "60s", - "keyfile": { - "filename": "etc/certs/client-key.pem", - "file": "" - }, - "mountpoint": "bridge/emqx/${node}/", - "password": "", - "pool_size": 8, - "proto_ver": "mqttv4", - "reconnect_interval": "30s", - "retry_interval": "20s", - "ssl": false, - "username": "" - }, - "created_at": 1616635569139, - "description": "brigde" - }, - { - "id": "rpc", - "type": "bridge_rpc", - "config": { - "address": "test@127.0.0.1", - "batch_size": 32, - "disk_cache": "off", - "mountpoint": "bridge/emqx/${node}/", - "pool_size": 8, - "reconnect_interval": "30s" - }, - "created_at": 1616635583552, - "description": "rpc" - } - ], - "blacklist": [], - "apps": [ - { - "id": "admin", - "secret": "public", - "name": "Default", - "desc": "Application user", - "status": true, - "expired": "undefined" - } - ], - "users": [ - { - "username": "admin", - "password": "q8v7hISIMz+iKn/ZuAaogvAxKbA=", - "tags": "administrator" - } - ], - "auth_mnesia": [], - "acl_mnesia": [], - "date": "2021-03-25 09:26:34" -} \ No newline at end of file diff --git a/apps/emqx_management/test/emqx_bridge_mqtt_data_export_import_SUITE_data/ee4010.json b/apps/emqx_management/test/emqx_bridge_mqtt_data_export_import_SUITE_data/ee4010.json deleted file mode 100644 index fedd35884..000000000 --- a/apps/emqx_management/test/emqx_bridge_mqtt_data_export_import_SUITE_data/ee4010.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "version": "4.0", - "users": [ - { - "username": "admin", - "tags": "administrator", - "password": "GCX5nvOMK0hbiMB4AUyc25wI8fU=" - } - ], - "schemas": [], - "rules": [], - "resources": [ - { - "type": "bridge_mqtt", - "id": "bridge", - "description": "bridge", - "created_at": null, - "config": { - "username": "user", - "ssl": "off", - "retry_interval": "20s", - "reconnect_interval": "30s", - "proto_ver": "mqttv4", - "password": "passwd", - "mountpoint": "bridge/emqx/${node}/", - "keyfile": "etc/certs/client-key.pem", - "keepalive": "60s", - "disk_cache": "off", - "clientid": "bridge_aws", - "ciphers": "ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384", - "certfile": "etc/certs/client-cert.pem", - "cacertfile": "etc/certs/cacert.pem", - "bridge_mode": true, - "address": "127.0.0.1:1883" - } - }, - { - "type": "bridge_rpc", - "id": "rpc", - "description": "rpc", - "created_at": null, - "config": { - "reconnect_interval": "30s", - "pool_size": 8, - "mountpoint": "bridge/emqx/${node}/", - "disk_cache": "off", - "batch_size": 32, - "address": "test@127.0.0.1" - } - } - ], - "date": "2021-04-13 13:57:23", - "blacklist": [], - "auth_username": [], - "auth_mnesia": [], - "auth_clientid": [], - "apps": [ - { - "status": true, - "secret": "public", - "name": "Default", - "id": "admin", - "expired": "undefined", - "desc": "Application user" - } - ], - "acl_mnesia": [] -} \ No newline at end of file diff --git a/apps/emqx_management/test/emqx_bridge_mqtt_data_export_import_SUITE_data/ee410.json b/apps/emqx_management/test/emqx_bridge_mqtt_data_export_import_SUITE_data/ee410.json deleted file mode 100644 index 8a6f7129b..000000000 --- a/apps/emqx_management/test/emqx_bridge_mqtt_data_export_import_SUITE_data/ee410.json +++ /dev/null @@ -1,70 +0,0 @@ -{ - "version": "4.1", - "users": [ - { - "username": "admin", - "tags": "administrator", - "password": "atdwlByxL9/9P3CoFJ60drhodkY=" - } - ], - "schemas": [], - "rules": [], - "resources": [ - { - "type": "bridge_rpc", - "id": "rpc", - "description": "rpc", - "created_at": null, - "config": { - "reconnect_interval": "30s", - "pool_size": 8, - "mountpoint": "bridge/emqx/${node}/", - "disk_cache": "off", - "batch_size": 32, - "address": "test@127.0.0.1" - } - }, - { - "type": "bridge_mqtt", - "id": "bridge", - "description": "bridge", - "created_at": null, - "config": { - "username": "", - "ssl": "off", - "retry_interval": "20s", - "reconnect_interval": "30s", - "proto_ver": "mqttv4", - "pool_size": 8, - "password": "", - "mountpoint": "bridge/emqx/${node}/", - "keyfile": "etc/certs/client-key.pem", - "keepalive": "60s", - "disk_cache": "off", - "clientid": "client", - "ciphers": "ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384,ECDHE-ECDSA-AES256-SHA384,ECDHE-RSA-AES256-SHA384,ECDHE-ECDSA-DES-CBC3-SHA,ECDH-ECDSA-AES256-GCM-SHA384,ECDH-RSA-AES256-GCM-SHA384,ECDH-ECDSA-AES256-SHA384,ECDH-RSA-AES256-SHA384,DHE-DSS-AES256-GCM-SHA384,DHE-DSS-AES256-SHA256,AES256-GCM-SHA384,AES256-SHA256,ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-RSA-AES128-GCM-SHA256,ECDHE-ECDSA-AES128-SHA256,ECDHE-RSA-AES128-SHA256,ECDH-ECDSA-AES128-GCM-SHA256,ECDH-RSA-AES128-GCM-SHA256,ECDH-ECDSA-AES128-SHA256,ECDH-RSA-AES128-SHA256,DHE-DSS-AES128-GCM-SHA256,DHE-DSS-AES128-SHA256,AES128-GCM-SHA256,AES128-SHA256,ECDHE-ECDSA-AES256-SHA,ECDHE-RSA-AES256-SHA,DHE-DSS-AES256-SHA,ECDH-ECDSA-AES256-SHA,ECDH-RSA-AES256-SHA,AES256-SHA,ECDHE-ECDSA-AES128-SHA,ECDHE-RSA-AES128-SHA,DHE-DSS-AES128-SHA,ECDH-ECDSA-AES128-SHA,ECDH-RSA-AES128-SHA,AES128-SHA", - "certfile": "etc/certs/client-cert.pem", - "cacertfile": "etc/certs/cacert.pem", - "bridge_mode": false, - "append": true, - "address": "127.0.0.1:1883" - } - } - ], - "date": "2021-04-13 11:30:21", - "blacklist": [], - "auth_username": [], - "auth_mnesia": [], - "auth_clientid": [], - "apps": [ - { - "status": true, - "secret": "public", - "name": "Default", - "id": "admin", - "expired": "undefined", - "desc": "Application user" - } - ], - "acl_mnesia": [] -} \ No newline at end of file diff --git a/apps/emqx_management/test/emqx_bridge_mqtt_data_export_import_SUITE_data/ee411.json b/apps/emqx_management/test/emqx_bridge_mqtt_data_export_import_SUITE_data/ee411.json deleted file mode 100644 index 6bbd3ac7b..000000000 --- a/apps/emqx_management/test/emqx_bridge_mqtt_data_export_import_SUITE_data/ee411.json +++ /dev/null @@ -1,70 +0,0 @@ -{ - "version": "4.1", - "users": [ - { - "username": "admin", - "tags": "administrator", - "password": "tsYtP3TylchkM7J7YTc46Di0kPk=" - } - ], - "schemas": [], - "rules": [], - "resources": [ - { - "type": "bridge_rpc", - "id": "rpc", - "description": "rpc", - "created_at": null, - "config": { - "reconnect_interval": "30s", - "pool_size": 8, - "mountpoint": "bridge/emqx/${node}/", - "disk_cache": "off", - "batch_size": 32, - "address": "test@127.0.0.1" - } - }, - { - "type": "bridge_mqtt", - "id": "bridge", - "description": "bridge", - "created_at": null, - "config": { - "username": "", - "ssl": "off", - "retry_interval": "20s", - "reconnect_interval": "30s", - "proto_ver": "mqttv4", - "pool_size": 8, - "password": "", - "mountpoint": "bridge/emqx/${node}/", - "keyfile": "etc/certs/client-key.pem", - "keepalive": "60s", - "disk_cache": "off", - "clientid": "client", - "ciphers": "ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384,ECDHE-ECDSA-AES256-SHA384,ECDHE-RSA-AES256-SHA384,ECDHE-ECDSA-DES-CBC3-SHA,ECDH-ECDSA-AES256-GCM-SHA384,ECDH-RSA-AES256-GCM-SHA384,ECDH-ECDSA-AES256-SHA384,ECDH-RSA-AES256-SHA384,DHE-DSS-AES256-GCM-SHA384,DHE-DSS-AES256-SHA256,AES256-GCM-SHA384,AES256-SHA256,ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-RSA-AES128-GCM-SHA256,ECDHE-ECDSA-AES128-SHA256,ECDHE-RSA-AES128-SHA256,ECDH-ECDSA-AES128-GCM-SHA256,ECDH-RSA-AES128-GCM-SHA256,ECDH-ECDSA-AES128-SHA256,ECDH-RSA-AES128-SHA256,DHE-DSS-AES128-GCM-SHA256,DHE-DSS-AES128-SHA256,AES128-GCM-SHA256,AES128-SHA256,ECDHE-ECDSA-AES256-SHA,ECDHE-RSA-AES256-SHA,DHE-DSS-AES256-SHA,ECDH-ECDSA-AES256-SHA,ECDH-RSA-AES256-SHA,AES256-SHA,ECDHE-ECDSA-AES128-SHA,ECDHE-RSA-AES128-SHA,DHE-DSS-AES128-SHA,ECDH-ECDSA-AES128-SHA,ECDH-RSA-AES128-SHA,AES128-SHA", - "certfile": "etc/certs/client-cert.pem", - "cacertfile": "etc/certs/cacert.pem", - "bridge_mode": false, - "append": true, - "address": "127.0.0.1:1883" - } - } - ], - "date": "2021-04-13 16:37:18", - "blacklist": [], - "auth_username": [], - "auth_mnesia": [], - "auth_clientid": [], - "apps": [ - { - "status": true, - "secret": "public", - "name": "Default", - "id": "admin", - "expired": "undefined", - "desc": "Application user" - } - ], - "acl_mnesia": [] -} \ No newline at end of file diff --git a/apps/emqx_management/test/emqx_bridge_mqtt_data_export_import_SUITE_data/ee420.json b/apps/emqx_management/test/emqx_bridge_mqtt_data_export_import_SUITE_data/ee420.json deleted file mode 100644 index 3b56495d1..000000000 --- a/apps/emqx_management/test/emqx_bridge_mqtt_data_export_import_SUITE_data/ee420.json +++ /dev/null @@ -1,119 +0,0 @@ -{ - "version": "4.2", - "date": "2021-04-13 11:35:13", - "modules": [ - { - "id": "module:b9294d70", - "type": "recon", - "config": {}, - "enabled": true, - "created_at": "undefined", - "description": "" - }, - { - "id": "module:c7c7b692", - "type": "presence", - "config": { - "qos": 0 - }, - "enabled": true, - "created_at": "undefined", - "description": "" - }, - { - "id": "module:486adc4b", - "type": "internal_acl", - "config": { - "acl_rule_file": "etc/acl.conf" - }, - "enabled": true, - "created_at": "undefined", - "description": "" - }, - { - "id": "module:411cf85d", - "type": "retainer", - "config": { - "storage_type": "ram", - "max_retained_messages": 0, - "max_payload_size": "1MB", - "expiry_interval": 0 - }, - "enabled": true, - "created_at": "undefined", - "description": "" - }, - { - "id": "module:127b92c3", - "type": "hot_confs", - "config": {}, - "enabled": true, - "created_at": "undefined", - "description": "" - } - ], - "rules": [], - "resources": [ - { - "id": "bridge", - "type": "bridge_mqtt", - "config": { - "address": "127.0.0.1:1883", - "append": true, - "bridge_mode": false, - "cacertfile": "etc/certs/cacert.pem", - "certfile": "etc/certs/client-cert.pem", - "ciphers": "ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384,ECDHE-ECDSA-AES256-SHA384,ECDHE-RSA-AES256-SHA384,ECDHE-ECDSA-DES-CBC3-SHA,ECDH-ECDSA-AES256-GCM-SHA384,ECDH-RSA-AES256-GCM-SHA384,ECDH-ECDSA-AES256-SHA384,ECDH-RSA-AES256-SHA384,DHE-DSS-AES256-GCM-SHA384,DHE-DSS-AES256-SHA256,AES256-GCM-SHA384,AES256-SHA256,ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-RSA-AES128-GCM-SHA256,ECDHE-ECDSA-AES128-SHA256,ECDHE-RSA-AES128-SHA256,ECDH-ECDSA-AES128-GCM-SHA256,ECDH-RSA-AES128-GCM-SHA256,ECDH-ECDSA-AES128-SHA256,ECDH-RSA-AES128-SHA256,DHE-DSS-AES128-GCM-SHA256,DHE-DSS-AES128-SHA256,AES128-GCM-SHA256,AES128-SHA256,ECDHE-ECDSA-AES256-SHA,ECDHE-RSA-AES256-SHA,DHE-DSS-AES256-SHA,ECDH-ECDSA-AES256-SHA,ECDH-RSA-AES256-SHA,AES256-SHA,ECDHE-ECDSA-AES128-SHA,ECDHE-RSA-AES128-SHA,DHE-DSS-AES128-SHA,ECDH-ECDSA-AES128-SHA,ECDH-RSA-AES128-SHA,AES128-SHA", - "clientid": "client", - "disk_cache": "off", - "keepalive": "60s", - "keyfile": "etc/certs/client-key.pem", - "mountpoint": "bridge/emqx/${node}/", - "password": "", - "pool_size": 8, - "proto_ver": "mqttv4", - "reconnect_interval": "30s", - "retry_interval": "20s", - "ssl": "off", - "username": "" - }, - "created_at": null, - "description": "bridge" - }, - { - "id": "rpc", - "type": "bridge_rpc", - "config": { - "address": "test@127.0.0.1", - "batch_size": 32, - "disk_cache": "off", - "mountpoint": "bridge/emqx/${node}/", - "pool_size": 8, - "reconnect_interval": "30s" - }, - "created_at": null, - "description": "rpc" - } - ], - "blacklist": [], - "apps": [ - { - "id": "admin", - "secret": "public", - "name": "Default", - "desc": "Application user", - "status": true, - "expired": "undefined" - } - ], - "users": [ - { - "username": "admin", - "password": "bx1P63qGDhKvZYdltxX4NVY2kS4=", - "tags": "administrator" - } - ], - "auth_mnesia": [], - "acl_mnesia": [], - "schemas": [] -} \ No newline at end of file diff --git a/apps/emqx_management/test/emqx_bridge_mqtt_data_export_import_SUITE_data/ee425.json b/apps/emqx_management/test/emqx_bridge_mqtt_data_export_import_SUITE_data/ee425.json deleted file mode 100644 index c6a80b9f6..000000000 --- a/apps/emqx_management/test/emqx_bridge_mqtt_data_export_import_SUITE_data/ee425.json +++ /dev/null @@ -1,123 +0,0 @@ -{ - "version": "4.2", - "date": "2021-04-13 16:42:34", - "modules": [ - { - "id": "module:3dc04a9c", - "type": "retainer", - "config": { - "storage_type": "ram", - "max_retained_messages": 0, - "max_payload_size": "1MB", - "expiry_interval": 0 - }, - "enabled": true, - "created_at": "undefined", - "description": "" - }, - { - "id": "module:5128901f", - "type": "recon", - "config": {}, - "enabled": true, - "created_at": "undefined", - "description": "" - }, - { - "id": "module:9d1596c8", - "type": "presence", - "config": { - "qos": 0 - }, - "enabled": true, - "created_at": "undefined", - "description": "" - }, - { - "id": "module:43d43410", - "type": "internal_acl", - "config": { - "acl_rule_file": "etc/acl.conf" - }, - "enabled": true, - "created_at": "undefined", - "description": "" - } - ], - "rules": [], - "resources": [ - { - "id": "bridge", - "type": "bridge_mqtt", - "config": { - "address": "127.0.0.1:1883", - "append": true, - "bridge_mode": false, - "cacertfile": { - "filename": "", - "file": "" - }, - "certfile": { - "filename": "", - "file": "" - }, - "ciphers": "ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384,ECDHE-ECDSA-AES256-SHA384,ECDHE-RSA-AES256-SHA384,ECDHE-ECDSA-DES-CBC3-SHA,ECDH-ECDSA-AES256-GCM-SHA384,ECDH-RSA-AES256-GCM-SHA384,ECDH-ECDSA-AES256-SHA384,ECDH-RSA-AES256-SHA384,DHE-DSS-AES256-GCM-SHA384,DHE-DSS-AES256-SHA256,AES256-GCM-SHA384,AES256-SHA256,ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-RSA-AES128-GCM-SHA256,ECDHE-ECDSA-AES128-SHA256,ECDHE-RSA-AES128-SHA256,ECDH-ECDSA-AES128-GCM-SHA256,ECDH-RSA-AES128-GCM-SHA256,ECDH-ECDSA-AES128-SHA256,ECDH-RSA-AES128-SHA256,DHE-DSS-AES128-GCM-SHA256,DHE-DSS-AES128-SHA256,AES128-GCM-SHA256,AES128-SHA256,ECDHE-ECDSA-AES256-SHA,ECDHE-RSA-AES256-SHA,DHE-DSS-AES256-SHA,ECDH-ECDSA-AES256-SHA,ECDH-RSA-AES256-SHA,AES256-SHA,ECDHE-ECDSA-AES128-SHA,ECDHE-RSA-AES128-SHA,DHE-DSS-AES128-SHA,ECDH-ECDSA-AES128-SHA,ECDH-RSA-AES128-SHA,AES128-SHA", - "clientid": "client", - "disk_cache": "off", - "keepalive": "60s", - "keyfile": { - "filename": "", - "file": "" - }, - "mountpoint": "bridge/emqx/${node}/", - "password": "", - "pool_size": 8, - "proto_ver": "mqttv4", - "reconnect_interval": "30s", - "retry_interval": "20s", - "ssl": false, - "username": "", - "verify": false - }, - "created_at": null, - "description": "bridge" - }, - { - "id": "rpc", - "type": "bridge_rpc", - "config": { - "address": "test@127.0.0.1", - "batch_size": 32, - "disk_cache": "off", - "mountpoint": "bridge/emqx/${node}/", - "pool_size": 8, - "reconnect_interval": "30s" - }, - "created_at": null, - "description": "rpc" - } - ], - "blacklist": [], - "apps": [ - { - "id": "admin", - "secret": "public", - "name": "Default", - "desc": "Application user", - "status": true, - "expired": "undefined" - } - ], - "users": [ - { - "username": "admin", - "password": "Hd8AMmbFs+LsqQXQxaV/WqLoGEk=", - "tags": "administrator" - } - ], - "auth_mnesia": [], - "acl_mnesia": [], - "schemas": [], - "configs": [], - "listeners_state": [] -} \ No newline at end of file diff --git a/apps/emqx_management/test/emqx_bridge_mqtt_data_export_import_SUITE_data/ee430.json b/apps/emqx_management/test/emqx_bridge_mqtt_data_export_import_SUITE_data/ee430.json deleted file mode 100644 index 2b7e66013..000000000 --- a/apps/emqx_management/test/emqx_bridge_mqtt_data_export_import_SUITE_data/ee430.json +++ /dev/null @@ -1,123 +0,0 @@ -{ - "version": "4.3", - "rules": [], - "resources": [ - { - "id": "bridge", - "type": "bridge_mqtt", - "config": { - "address": "127.0.0.1:1883", - "append": true, - "bridge_mode": false, - "cacertfile": { - "filename": "", - "file": "" - }, - "certfile": { - "filename": "", - "file": "" - }, - "ciphers": "ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384,ECDHE-ECDSA-AES256-SHA384,ECDHE-RSA-AES256-SHA384,ECDHE-ECDSA-DES-CBC3-SHA,ECDH-ECDSA-AES256-GCM-SHA384,ECDH-RSA-AES256-GCM-SHA384,ECDH-ECDSA-AES256-SHA384,ECDH-RSA-AES256-SHA384,DHE-DSS-AES256-GCM-SHA384,DHE-DSS-AES256-SHA256,AES256-GCM-SHA384,AES256-SHA256,ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-RSA-AES128-GCM-SHA256,ECDHE-ECDSA-AES128-SHA256,ECDHE-RSA-AES128-SHA256,ECDH-ECDSA-AES128-GCM-SHA256,ECDH-RSA-AES128-GCM-SHA256,ECDH-ECDSA-AES128-SHA256,ECDH-RSA-AES128-SHA256,DHE-DSS-AES128-GCM-SHA256,DHE-DSS-AES128-SHA256,AES128-GCM-SHA256,AES128-SHA256,ECDHE-ECDSA-AES256-SHA,ECDHE-RSA-AES256-SHA,DHE-DSS-AES256-SHA,ECDH-ECDSA-AES256-SHA,ECDH-RSA-AES256-SHA,AES256-SHA,ECDHE-ECDSA-AES128-SHA,ECDHE-RSA-AES128-SHA,DHE-DSS-AES128-SHA,ECDH-ECDSA-AES128-SHA,ECDH-RSA-AES128-SHA,AES128-SHA", - "clientid": "client", - "disk_cache": "off", - "keepalive": "60s", - "keyfile": { - "filename": "", - "file": "" - }, - "mountpoint": "bridge/emqx/${node}/", - "password": "", - "pool_size": 8, - "proto_ver": "mqttv4", - "reconnect_interval": "30s", - "retry_interval": "20s", - "ssl": false, - "username": "", - "verify": false - }, - "created_at": 1618304391051, - "description": "bridge" - }, - { - "id": "rpc", - "type": "bridge_rpc", - "config": { - "address": "test@127.0.0.1", - "batch_size": 32, - "disk_cache": "off", - "mountpoint": "bridge/emqx/${node}/", - "pool_size": 8, - "reconnect_interval": "30s" - }, - "created_at": 1618304406842, - "description": "rpc" - } - ], - "blacklist": [], - "apps": [ - { - "id": "admin", - "secret": "public", - "name": "Default", - "desc": "Application user", - "status": true, - "expired": "undefined" - } - ], - "users": [ - { - "username": "admin", - "password": "qq8hg9pOkmYiHqzi3+bcUaK2CGA=", - "tags": "administrator" - } - ], - "auth_mnesia": [], - "acl_mnesia": [], - "modules": [ - { - "id": "module:aabeddbf", - "type": "recon", - "config": {}, - "enabled": true, - "created_at": 1618304311061, - "description": "" - }, - { - "id": "module:cbe6d976", - "type": "internal_acl", - "config": { - "acl_rule_file": "etc/acl.conf" - }, - "enabled": true, - "created_at": 1618304311061, - "description": "" - }, - { - "id": "module:46375e06", - "type": "retainer", - "config": { - "storage_type": "ram", - "max_retained_messages": 0, - "max_payload_size": "1MB", - "expiry_interval": 0 - }, - "enabled": true, - "created_at": 1618304311061, - "description": "" - }, - { - "id": "module:091eb7c3", - "type": "presence", - "config": { - "qos": 0 - }, - "enabled": true, - "created_at": 1618304311061, - "description": "" - } - ], - "schemas": [], - "configs": [], - "listeners_state": [], - "date": "2021-04-13 17:59:52" -} \ No newline at end of file diff --git a/apps/emqx_management/test/emqx_mgmt_api_SUITE.erl b/apps/emqx_management/test/emqx_mgmt_api_SUITE.erl index 22d4f51ef..d372596ed 100644 --- a/apps/emqx_management/test/emqx_mgmt_api_SUITE.erl +++ b/apps/emqx_management/test/emqx_mgmt_api_SUITE.erl @@ -537,39 +537,6 @@ t_stats(_) -> ?assertEqual(<<"undefined">>, get(<<"message">>, Return)), meck:unload(emqx_mgmt). -t_data(_) -> - ok = emqx_rule_registry:mnesia(boot), - ok = emqx_dashboard_admin:mnesia(boot), - application:ensure_all_started(emqx_rule_engine), - application:ensure_all_started(emqx_dashboard), - {ok, Data} = request_api(post, api_path(["data","export"]), [], auth_header_(), [#{}]), - #{<<"filename">> := Filename, <<"node">> := Node} = emqx_ct_http:get_http_data(Data), - {ok, DataList} = request_api(get, api_path(["data","export"]), auth_header_()), - ?assertEqual(true, lists:member(emqx_ct_http:get_http_data(Data), emqx_ct_http:get_http_data(DataList))), - - ?assertMatch({ok, _}, request_api(post, api_path(["data","import"]), [], auth_header_(), #{<<"filename">> => Filename, <<"node">> => Node})), - ?assertMatch({ok, _}, request_api(post, api_path(["data","import"]), [], auth_header_(), #{<<"filename">> => Filename})), - application:stop(emqx_rule_engine), - application:stop(emqx_dahboard), - ok. - -t_data_import_content(_) -> - ok = emqx_rule_registry:mnesia(boot), - ok = emqx_dashboard_admin:mnesia(boot), - application:ensure_all_started(emqx_rule_engine), - application:ensure_all_started(emqx_dashboard), - {ok, Data} = request_api(post, api_path(["data","export"]), [], auth_header_(), [#{}]), - #{<<"filename">> := Filename} = emqx_ct_http:get_http_data(Data), - Dir = emqx:get_env(data_dir), - {ok, Bin} = file:read_file(filename:join(Dir, Filename)), - Content = emqx_json:decode(Bin), - %% TODO: enable when 5.0 if we are still using data export/import - %?assertMatch({ok, "{\"code\":0}"}, request_api(post, api_path(["data","import"]), [], auth_header_(), Content)), - ?assertMatch({ok, "{\"message\":\"5.0\",\"code\":\"unsupported_version\"}"}, - request_api(post, api_path(["data","import"]), [], auth_header_(), Content)), - application:stop(emqx_rule_engine), - application:stop(emqx_dahboard). - request_api(Method, Url, Auth) -> request_api(Method, Url, [], Auth, []). diff --git a/apps/emqx_management/test/emqx_webhook_data_export_import_SUITE.erl b/apps/emqx_management/test/emqx_webhook_data_export_import_SUITE.erl deleted file mode 100644 index 2965b7ad0..000000000 --- a/apps/emqx_management/test/emqx_webhook_data_export_import_SUITE.erl +++ /dev/null @@ -1,194 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_webhook_data_export_import_SUITE). --include_lib("eunit/include/eunit.hrl"). --include_lib("emqx_rule_engine/include/rule_engine.hrl"). --compile([export_all, nowarn_export_all]). - -% -define(EMQX_ENTERPRISE, true). - -%%-------------------------------------------------------------------- -%% Setups -%%-------------------------------------------------------------------- -all() -> - emqx_ct:all(?MODULE). - -init_per_suite(Cfg) -> - application:load(emqx_modules), - application:load(emqx_web_hook), - emqx_ct_helpers:start_apps([emqx_rule_engine, emqx_management]), - ok = emqx_rule_registry:mnesia(boot), - ok = emqx_rule_engine:load_providers(), - Cfg. - -end_per_suite(Cfg) -> - emqx_ct_helpers:stop_apps([emqx_management, emqx_rule_engine]), - Cfg. - -get_data_path() -> - emqx_ct_helpers:deps_path(emqx_management, "test/emqx_webhook_data_export_import_SUITE_data/"). - -remove_resource(Id) -> - emqx_rule_registry:remove_resource(Id), - emqx_rule_registry:remove_resource_params(Id). - -import(FilePath, Version) -> - ok = emqx_mgmt_data_backup:import(get_data_path() ++ "/" ++ FilePath, <<"{}">>), - lists:foreach(fun(#resource{id = Id, config = Config} = _Resource) -> - case Id of - <<"webhook">> -> - test_utils:resource_is_alive(Id), - handle_config(Config, Version), - remove_resource(Id); - _ -> ok - end - end, emqx_rule_registry:get_resources()). - -%%-------------------------------------------------------------------- -%% Cases -%%-------------------------------------------------------------------- --ifdef(EMQX_ENTERPRISE). - -t_importee4010(_) -> - import("ee4010.json", ee4010), - {ok, _} = emqx_mgmt_data_backup:export(). - -t_importee410(_) -> - import("ee410.json", ee410), - {ok, _} = emqx_mgmt_data_backup:export(). - -t_importee411(_) -> - import("ee411.json", ee411), - {ok, _} = emqx_mgmt_data_backup:export(). - -t_importee420(_) -> - import("ee420.json", ee420), - {ok, _} = emqx_mgmt_data_backup:export(). - -t_importee425(_) -> - import("ee425.json", ee425), - {ok, _} = emqx_mgmt_data_backup:export(). - -t_importee430(_) -> - import("ee430.json", ee430), - {ok, _} = emqx_mgmt_data_backup:export(). - -%%-------------------------------------------------------------------- -%% handle_config -%%-------------------------------------------------------------------- -handle_config(Config, 409) -> - handle_config(Config, 422); - -handle_config(Config, 415) -> - handle_config(Config, 422); - -handle_config(Config, 422) -> - ?assertEqual(<<"http://www.emqx.io">>, maps:get(<<"url">>, Config)), - ?assertEqual(<<"POST">>, maps:get(<<"method">>, Config)), - ?assertEqual(#{"k" => "v"}, maps:get(<<"headers">>, Config)); - -handle_config(Config, 423) -> - ?assertEqual(<<"http://www.emqx.io">>, maps:get(<<"url">>, Config)), - ?assertEqual(<<"POST">>, maps:get(<<"method">>, Config)), - ?assertEqual("application/json", maps:get(<<"content_type">>, Config)), - ?assertEqual(#{"k" => "v"}, maps:get(<<"headers">>, Config)); - -handle_config(Config, 425) -> - ?assertEqual(<<"http://www.emqx.io">>, maps:get(<<"url">>, Config)), - ?assertEqual(<<"POST">>, maps:get(<<"method">>, Config)), - ?assertEqual(#{"k" => "v"}, maps:get(<<"headers">>, Config)), - ?assertEqual(5, maps:get(<<"connect_timeout">>, Config)), - ?assertEqual(5, maps:get(<<"request_timeout">>, Config)), - ?assertEqual(8, maps:get(<<"pool_size">>, Config)); - -handle_config(Config, 430) -> - ?assertEqual(<<"http://www.emqx.io">>, maps:get(<<"url">>, Config)), - ?assertEqual(<<"POST">>, maps:get(<<"method">>, Config)), - ?assertEqual(#{"k" => "v"}, maps:get(<<"headers">>, Config)), - ?assertEqual("5s", maps:get(<<"connect_timeout">>, Config)), - ?assertEqual("5s", maps:get(<<"request_timeout">>, Config)), - ?assertEqual(false, maps:get(<<"verify">>, Config)), - ?assertEqual(true, is_map(maps:get(<<"cacertfile">>, Config))), - ?assertEqual(true, is_map(maps:get(<<"certfile">>, Config))), - ?assertEqual(true, is_map(maps:get(<<"keyfile">>, Config))), - ?assertEqual(8, maps:get(<<"pool_size">>, Config)); -handle_config(_, _) -> ok. --endif. - --ifndef(EMQX_ENTERPRISE). - -t_import422(_) -> - import("422.json", 422), - {ok, _} = emqx_mgmt_data_backup:export(). - -t_import423(_) -> - import("423.json", 423), - {ok, _} = emqx_mgmt_data_backup:export(). - -t_import425(_) -> - import("425.json", 425), - {ok, _} = emqx_mgmt_data_backup:export(). - -t_import430(_) -> - import("430.json", 430), - {ok, _} = emqx_mgmt_data_backup:export(). - -t_import409(_) -> - import("409.json", 409), - {ok, _} = emqx_mgmt_data_backup:export(). - -t_import415(_) -> - import("415.json", 415), - {ok, _} = emqx_mgmt_data_backup:export(). - -%%-------------------------------------------------------------------- -%% handle_config -%%-------------------------------------------------------------------- - -handle_config(Config, ee4010) -> - ?assertEqual(<<"http://www.emqx.io">>, maps:get(<<"url">>, Config)); - -handle_config(Config, ee410) -> - ?assertEqual(<<"http://www.emqx.io">>, maps:get(<<"url">>, Config)); - -handle_config(Config, ee411) -> - ?assertEqual(<<"http://www.emqx.io">>, maps:get(<<"url">>, Config)); - -handle_config(Config, ee420) -> - ?assertEqual(<<"http://www.emqx.io">>, maps:get(<<"url">>, Config)); - -handle_config(Config, ee425) -> - ?assertEqual(<<"http://www.emqx.io">>, maps:get(<<"url">>, Config)), - ?assertEqual(<<"5s">>, maps:get(<<"connect_timeout">>, Config)), - ?assertEqual(<<"5s">>, maps:get(<<"request_timeout">>, Config)), - ?assertEqual(false, maps:get(<<"verify">>, Config)), - ?assertEqual(8, maps:get(<<"pool_size">>, Config)); - -handle_config(Config, ee435) -> - handle_config(Config, ee430); - -handle_config(Config, ee430) -> - ?assertEqual(<<"http://www.emqx.io">>, maps:get(<<"url">>, Config)), - ?assertEqual(<<"5s">>, maps:get(<<"connect_timeout">>, Config)), - ?assertEqual(<<"5s">>, maps:get(<<"request_timeout">>, Config)), - ?assertEqual(false, maps:get(<<"verify">>, Config)), - ?assertEqual(8, maps:get(<<"pool_size">>, Config)); - -handle_config(Config, _) -> - io:format("|>=> :~p~n", [Config]). - --endif. diff --git a/apps/emqx_management/test/emqx_webhook_data_export_import_SUITE_data/409.json b/apps/emqx_management/test/emqx_webhook_data_export_import_SUITE_data/409.json deleted file mode 100644 index 01591e107..000000000 --- a/apps/emqx_management/test/emqx_webhook_data_export_import_SUITE_data/409.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "version": "4.0", - "users": [ - { - "username": "admin", - "tags": "administrator", - "password": "m/GtjNgri9GILklefVJH8BeTnNE=" - } - ], - "schemas": [], - "rules": [], - "resources": [ - { - "type": "web_hook", - "id": "webhook", - "description": "webhook", - "created_at": null, - "config": { - "headers": { - "k": "v" - }, - "method": "POST", - "url": "http://www.emqx.io" - } - } - ], - "date": "2021-03-30 13:38:39", - "blacklist": [], - "auth_username": [], - "auth_mnesia": [], - "auth_clientid": [], - "apps": [ - { - "status": true, - "secret": "public", - "name": "Default", - "id": "admin", - "expired": "undefined", - "desc": "Application user" - } - ], - "acl_mnesia": [] -} \ No newline at end of file diff --git a/apps/emqx_management/test/emqx_webhook_data_export_import_SUITE_data/415.json b/apps/emqx_management/test/emqx_webhook_data_export_import_SUITE_data/415.json deleted file mode 100644 index ca6d7dd05..000000000 --- a/apps/emqx_management/test/emqx_webhook_data_export_import_SUITE_data/415.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "version": "4.1", - "users": [ - { - "username": "admin", - "tags": "administrator", - "password": "3W+nHOkCZLFspENkIvHKzCbHxHI=" - } - ], - "schemas": [], - "rules": [], - "resources": [ - { - "type": "web_hook", - "id": "webhook", - "description": "webhook", - "created_at": null, - "config": { - "url": "http://www.emqx.io", - "method": "POST", - "headers": { - "k": "v" - } - } - } - ], - "date": "2021-03-30 13:52:53", - "blacklist": [], - "auth_username": [], - "auth_clientid": [], - "apps": [ - { - "status": true, - "secret": "public", - "name": "Default", - "id": "admin", - "expired": "undefined", - "desc": "Application user" - } - ], - "acl_mnesia": [] -} \ No newline at end of file diff --git a/apps/emqx_management/test/emqx_webhook_data_export_import_SUITE_data/422.json b/apps/emqx_management/test/emqx_webhook_data_export_import_SUITE_data/422.json deleted file mode 100644 index 4bddd6344..000000000 --- a/apps/emqx_management/test/emqx_webhook_data_export_import_SUITE_data/422.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "version": "4.2", - "date": "2021-03-23 23:22:30", - "rules": [], - "resources": [ - { - "id": "webhook", - "type": "web_hook", - "config": { - "headers": { - "k": "v" - }, - "method": "POST", - "url": "http://www.emqx.io" - }, - "created_at": null, - "description": "webhook" - } - ], - "blacklist": [], - "apps": [ - { - "id": "admin", - "secret": "public", - "name": "Default", - "desc": "Application user", - "status": true, - "expired": "undefined" - } - ], - "users": [ - { - "username": "admin", - "password": "mOTRLWt85F7GW+CSiBuRUZiRANw=", - "tags": "administrator" - } - ], - "auth_clientid": [], - "auth_username": [], - "auth_mnesia": [], - "acl_mnesia": [], - "schemas": [] -} \ No newline at end of file diff --git a/apps/emqx_management/test/emqx_webhook_data_export_import_SUITE_data/423.json b/apps/emqx_management/test/emqx_webhook_data_export_import_SUITE_data/423.json deleted file mode 100644 index c9f8edbad..000000000 --- a/apps/emqx_management/test/emqx_webhook_data_export_import_SUITE_data/423.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "version": "4.2", - "date": "2021-03-23 23:29:24", - "rules": [], - "resources": [ - { - "id": "webhook", - "type": "web_hook", - "config": { - "headers": { - "k": "v" - }, - "method": "POST", - "url": "http://www.emqx.io" - }, - "content_type": "application/json", - "created_at": null, - "description": "webhook" - } - ], - "blacklist": [], - "apps": [ - { - "id": "admin", - "secret": "public", - "name": "Default", - "desc": "Application user", - "status": true, - "expired": "undefined" - } - ], - "users": [ - { - "username": "admin", - "password": "mOTRLWt85F7GW+CSiBuRUZiRANw=", - "tags": "administrator" - } - ], - "auth_clientid": [], - "auth_username": [], - "auth_mnesia": [], - "acl_mnesia": [], - "schemas": [] -} \ No newline at end of file diff --git a/apps/emqx_management/test/emqx_webhook_data_export_import_SUITE_data/425.json b/apps/emqx_management/test/emqx_webhook_data_export_import_SUITE_data/425.json deleted file mode 100644 index 7893c4624..000000000 --- a/apps/emqx_management/test/emqx_webhook_data_export_import_SUITE_data/425.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "version": "4.2", - "date": "2021-03-24 14:51:22", - "rules": [], - "resources": [ - { - "id": "webhook", - "type": "web_hook", - "config": { - "headers": { - "k": "v" - }, - "method": "POST", - "url": "http://www.emqx.io" - }, - "content_type": "application/json", - "connect_timeout" : 5, - "request_timeout" : 5, - "pool_size" : 8, - "created_at": null, - "description": "webhook" - } - ], - "blacklist": [], - "apps": [ - { - "id": "admin", - "secret": "public", - "name": "Default", - "desc": "Application user", - "status": true, - "expired": "undefined" - } - ], - "users": [ - { - "username": "admin", - "password": "mOTRLWt85F7GW+CSiBuRUZiRANw=", - "tags": "administrator" - } - ], - "auth_clientid": [], - "auth_username": [], - "auth_mnesia": [], - "acl_mnesia": [], - "schemas": [] -} \ No newline at end of file diff --git a/apps/emqx_management/test/emqx_webhook_data_export_import_SUITE_data/430.json b/apps/emqx_management/test/emqx_webhook_data_export_import_SUITE_data/430.json deleted file mode 100644 index 9472152f9..000000000 --- a/apps/emqx_management/test/emqx_webhook_data_export_import_SUITE_data/430.json +++ /dev/null @@ -1,52 +0,0 @@ -{ - "version": "4.3", - "rules": [], - "resources": [ - { - "id": "webhook", - "type": "web_hook", - "config": { - "cacertfile": { - "filename": "", - "file": "" - }, - "certfile": { - "filename": "", - "file": "" - }, - "keyfile": { - "filename": "", - "file": "" - }, - "connect_timeout": "5s", - "pool_size": 8, - "request_timeout": "5s", - "url": "http://www.emqx.io", - "verify": false - }, - "created_at": 1616581851001, - "description": "webhook" - } - ], - "blacklist": [], - "apps": [ - { - "id": "admin", - "secret": "public", - "name": "Default", - "desc": "Application user", - "status": true, - "expired": "undefined" - } - ], - "users": [ - { - "username": "admin", - "password": "q8v7hISIMz+iKn/ZuAaogvAxKbA=", - "tags": "administrator" - } - ], - "auth_mnesia": [], - "acl_mnesia": [], - "date": "2021-03-24 18:31:21" -} \ No newline at end of file diff --git a/apps/emqx_management/test/emqx_webhook_data_export_import_SUITE_data/ee4010.json b/apps/emqx_management/test/emqx_webhook_data_export_import_SUITE_data/ee4010.json deleted file mode 100644 index d979d1d0d..000000000 --- a/apps/emqx_management/test/emqx_webhook_data_export_import_SUITE_data/ee4010.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "version": "4.0", - "users": [ - { - "username": "admin", - "tags": "administrator", - "password": "GCX5nvOMK0hbiMB4AUyc25wI8fU=" - } - ], - "schemas": [], - "rules": [], - "resources": [ - { - "type": "web_hook", - "id": "web_hook", - "description": "webhook", - "created_at": null, - "config": { - "url": "http://www.emqx.io", - "method": "POST", - "headers": { - "k": "v" - } - } - } - ], - "date": "2021-04-13 13:57:23", - "blacklist": [], - "auth_username": [], - "auth_mnesia": [], - "auth_clientid": [], - "apps": [ - { - "status": true, - "secret": "public", - "name": "Default", - "id": "admin", - "expired": "undefined", - "desc": "Application user" - } - ], - "acl_mnesia": [] -} \ No newline at end of file diff --git a/apps/emqx_management/test/emqx_webhook_data_export_import_SUITE_data/ee410.json b/apps/emqx_management/test/emqx_webhook_data_export_import_SUITE_data/ee410.json deleted file mode 100644 index e97427e01..000000000 --- a/apps/emqx_management/test/emqx_webhook_data_export_import_SUITE_data/ee410.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "version": "4.1", - "users": [ - { - "username": "admin", - "tags": "administrator", - "password": "atdwlByxL9/9P3CoFJ60drhodkY=" - } - ], - "schemas": [], - "rules": [], - "resources": [ - { - "type": "web_hook", - "id": "webhook", - "description": "webhook", - "created_at": null, - "config": { - "url": "http://www.emqx.io", - "method": "POST", - "headers": { - "k": "v" - } - } - } - ], - "date": "2021-04-13 11:30:21", - "blacklist": [], - "auth_username": [], - "auth_mnesia": [], - "auth_clientid": [], - "apps": [ - { - "status": true, - "secret": "public", - "name": "Default", - "id": "admin", - "expired": "undefined", - "desc": "Application user" - } - ], - "acl_mnesia": [] -} \ No newline at end of file diff --git a/apps/emqx_management/test/emqx_webhook_data_export_import_SUITE_data/ee411.json b/apps/emqx_management/test/emqx_webhook_data_export_import_SUITE_data/ee411.json deleted file mode 100644 index ed7fab854..000000000 --- a/apps/emqx_management/test/emqx_webhook_data_export_import_SUITE_data/ee411.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "version": "4.1", - "users": [ - { - "username": "admin", - "tags": "administrator", - "password": "tsYtP3TylchkM7J7YTc46Di0kPk=" - } - ], - "schemas": [], - "rules": [], - "resources": [ - { - "type": "web_hook", - "id": "web_hook", - "description": "webhook", - "created_at": null, - "config": { - "url": "http://www.emqx.io", - "method": "POST", - "headers": { - "k": "v" - } - } - } - ], - "date": "2021-04-13 16:37:18", - "blacklist": [], - "auth_username": [], - "auth_mnesia": [], - "auth_clientid": [], - "apps": [ - { - "status": true, - "secret": "public", - "name": "Default", - "id": "admin", - "expired": "undefined", - "desc": "Application user" - } - ], - "acl_mnesia": [] -} \ No newline at end of file diff --git a/apps/emqx_management/test/emqx_webhook_data_export_import_SUITE_data/ee420.json b/apps/emqx_management/test/emqx_webhook_data_export_import_SUITE_data/ee420.json deleted file mode 100644 index 52ad0c83f..000000000 --- a/apps/emqx_management/test/emqx_webhook_data_export_import_SUITE_data/ee420.json +++ /dev/null @@ -1,92 +0,0 @@ -{ - "version": "4.2", - "date": "2021-04-13 11:35:13", - "modules": [ - { - "id": "module:b9294d70", - "type": "recon", - "config": {}, - "enabled": true, - "created_at": "undefined", - "description": "" - }, - { - "id": "module:c7c7b692", - "type": "presence", - "config": { - "qos": 0 - }, - "enabled": true, - "created_at": "undefined", - "description": "" - }, - { - "id": "module:486adc4b", - "type": "internal_acl", - "config": { - "acl_rule_file": "etc/acl.conf" - }, - "enabled": true, - "created_at": "undefined", - "description": "" - }, - { - "id": "module:411cf85d", - "type": "retainer", - "config": { - "storage_type": "ram", - "max_retained_messages": 0, - "max_payload_size": "1MB", - "expiry_interval": 0 - }, - "enabled": true, - "created_at": "undefined", - "description": "" - }, - { - "id": "module:127b92c3", - "type": "hot_confs", - "config": {}, - "enabled": true, - "created_at": "undefined", - "description": "" - } - ], - "rules": [], - "resources": [ - { - "id": "webhook", - "type": "web_hook", - "config": { - "headers": { - "k": "v" - }, - "method": "POST", - "url": "http://www.emqx.io" - }, - "created_at": null, - "description": "webhook" - } - ], - "blacklist": [], - "apps": [ - { - "id": "admin", - "secret": "public", - "name": "Default", - "desc": "Application user", - "status": true, - "expired": "undefined" - } - ], - "users": [ - { - "username": "admin", - "password": "bx1P63qGDhKvZYdltxX4NVY2kS4=", - "tags": "administrator" - } - ], - "auth_mnesia": [], - "acl_mnesia": [], - "schemas": [] -} \ No newline at end of file diff --git a/apps/emqx_management/test/emqx_webhook_data_export_import_SUITE_data/ee425.json b/apps/emqx_management/test/emqx_webhook_data_export_import_SUITE_data/ee425.json deleted file mode 100644 index 46a1870bf..000000000 --- a/apps/emqx_management/test/emqx_webhook_data_export_import_SUITE_data/ee425.json +++ /dev/null @@ -1,102 +0,0 @@ -{ - "version": "4.2", - "date": "2021-04-13 16:42:34", - "modules": [ - { - "id": "module:3dc04a9c", - "type": "retainer", - "config": { - "storage_type": "ram", - "max_retained_messages": 0, - "max_payload_size": "1MB", - "expiry_interval": 0 - }, - "enabled": true, - "created_at": "undefined", - "description": "" - }, - { - "id": "module:5128901f", - "type": "recon", - "config": {}, - "enabled": true, - "created_at": "undefined", - "description": "" - }, - { - "id": "module:9d1596c8", - "type": "presence", - "config": { - "qos": 0 - }, - "enabled": true, - "created_at": "undefined", - "description": "" - }, - { - "id": "module:43d43410", - "type": "internal_acl", - "config": { - "acl_rule_file": "etc/acl.conf" - }, - "enabled": true, - "created_at": "undefined", - "description": "" - } - ], - "rules": [], - "resources": [ - { - "id": "webhook", - "type": "web_hook", - "config": { - "cacertfile": { - "filename": "", - "file": "" - }, - "certfile": { - "filename": "", - "file": "" - }, - "connect_timeout": "5s", - "headers": { - "k": "v" - }, - "keyfile": { - "filename": "", - "file": "" - }, - "method": "POST", - "pool_size": 8, - "request_timeout": "5s", - "url": "http://www.emqx.io", - "verify": false - }, - "created_at": null, - "description": "webhook" - } - ], - "blacklist": [], - "apps": [ - { - "id": "admin", - "secret": "public", - "name": "Default", - "desc": "Application user", - "status": true, - "expired": "undefined" - } - ], - "users": [ - { - "username": "admin", - "password": "Hd8AMmbFs+LsqQXQxaV/WqLoGEk=", - "tags": "administrator" - } - ], - "auth_mnesia": [], - "acl_mnesia": [], - "schemas": [], - "configs": [], - "listeners_state": [] -} \ No newline at end of file diff --git a/apps/emqx_management/test/emqx_webhook_data_export_import_SUITE_data/ee430.json b/apps/emqx_management/test/emqx_webhook_data_export_import_SUITE_data/ee430.json deleted file mode 100644 index 36986f24b..000000000 --- a/apps/emqx_management/test/emqx_webhook_data_export_import_SUITE_data/ee430.json +++ /dev/null @@ -1,98 +0,0 @@ -{ - "version": "4.3", - "rules": [], - "resources": [ - { - "id": "webhook", - "type": "web_hook", - "config": { - "cacertfile": { - "filename": "", - "file": "" - }, - "certfile": { - "filename": "", - "file": "" - }, - "connect_timeout": "5s", - "keyfile": { - "filename": "", - "file": "" - }, - "pool_size": 8, - "request_timeout": "5s", - "url": "http://www.emqx.io", - "verify": false - }, - "created_at": 1618304340172, - "description": "webhook" - } - ], - "blacklist": [], - "apps": [ - { - "id": "admin", - "secret": "public", - "name": "Default", - "desc": "Application user", - "status": true, - "expired": "undefined" - } - ], - "users": [ - { - "username": "admin", - "password": "qq8hg9pOkmYiHqzi3+bcUaK2CGA=", - "tags": "administrator" - } - ], - "auth_mnesia": [], - "acl_mnesia": [], - "modules": [ - { - "id": "module:aabeddbf", - "type": "recon", - "config": {}, - "enabled": true, - "created_at": 1618304311061, - "description": "" - }, - { - "id": "module:cbe6d976", - "type": "internal_acl", - "config": { - "acl_rule_file": "etc/acl.conf" - }, - "enabled": true, - "created_at": 1618304311061, - "description": "" - }, - { - "id": "module:46375e06", - "type": "retainer", - "config": { - "storage_type": "ram", - "max_retained_messages": 0, - "max_payload_size": "1MB", - "expiry_interval": 0 - }, - "enabled": true, - "created_at": 1618304311061, - "description": "" - }, - { - "id": "module:091eb7c3", - "type": "presence", - "config": { - "qos": 0 - }, - "enabled": true, - "created_at": 1618304311061, - "description": "" - } - ], - "schemas": [], - "configs": [], - "listeners_state": [], - "date": "2021-04-13 17:59:52" -} \ No newline at end of file From f227f5c0230cb67dfbc647dbedfd740bba883b34 Mon Sep 17 00:00:00 2001 From: Rory Z Date: Fri, 25 Jun 2021 13:52:41 +0800 Subject: [PATCH 032/174] chore(CI): delete compatibility test suite --- .github/workflows/run_cts_tests.yaml | 407 --------------------------- 1 file changed, 407 deletions(-) delete mode 100644 .github/workflows/run_cts_tests.yaml diff --git a/.github/workflows/run_cts_tests.yaml b/.github/workflows/run_cts_tests.yaml deleted file mode 100644 index 487d8fae7..000000000 --- a/.github/workflows/run_cts_tests.yaml +++ /dev/null @@ -1,407 +0,0 @@ -name: Compatibility Test Suite - -on: - push: - tags: - - v* - - e* - pull_request: - -jobs: - ldap: - runs-on: ubuntu-20.04 - - strategy: - fail-fast: false - matrix: - ldap_tag: - - 2.4.50 - network_type: - - ipv4 - - ipv6 - - steps: - - uses: actions/checkout@v1 - - name: docker compose up - env: - LDAP_TAG: ${{ matrix.ldap_tag }} - run: | - docker-compose \ - -f .ci/docker-compose-file/docker-compose-ldap-tcp.yaml \ - -f .ci/docker-compose-file/docker-compose.yaml \ - up -d --build - - name: setup - if: matrix.network_type == 'ipv4' - run: | - echo EMQX_AUTH__LDAP__SERVERS=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' ldap) >> "$GITHUB_ENV" - - name: setup - if: matrix.network_type == 'ipv6' - run: | - echo EMQX_AUTH__LDAP__SERVERS=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.GlobalIPv6Address}}{{end}}' ldap) >> "$GITHUB_ENV" - - name: set git token - run: | - if make emqx-ee --dry-run > /dev/null 2>&1; then - docker exec -i erlang bash -c "echo \"https://ci%40emqx.io:${{ secrets.CI_GIT_TOKEN }}@github.com\" > /root/.git-credentials && git config --global credential.helper store" - fi - - name: run test cases - run: | - export CUTTLEFISH_ENV_OVERRIDE_PREFIX=EMQX_ - export HOCON_ENV_OVERRIDE_PREFIX=EMQX_ - printenv > .env - docker exec -i erlang sh -c "make ensure-rebar3" - docker exec -i erlang sh -c "./rebar3 eunit --application=emqx_auth_ldap" - docker exec --env-file .env -i erlang sh -c "make apps/emqx_auth_ldap-ct" - - uses: actions/upload-artifact@v1 - if: failure() - with: - name: logs_ldap${{ matrix.ldap_tag }}_${{ matrix.network_type }} - path: _build/test/logs - - mongo: - runs-on: ubuntu-20.04 - - strategy: - fail-fast: false - matrix: - mongo_tag: - - 3 - - 4 - network_type: - - ipv4 - - ipv6 - connect_type: - - tls - - tcp - - steps: - - uses: actions/checkout@v1 - - name: docker-compose up - run: | - docker-compose \ - -f .ci/docker-compose-file/docker-compose-mongo-${{ matrix.connect_type }}.yaml \ - -f .ci/docker-compose-file/docker-compose.yaml \ - up -d --build - - name: setup - env: - MONGO_TAG: ${{ matrix.mongo_tag }} - if: matrix.connect_type == 'tls' - run: | - cat <<-EOF >> "$GITHUB_ENV" - EMQX_AUTH__MONGO__SSL__ENABLE=on - EMQX_AUTH__MONGO__SSL__CACERTFILE=/emqx/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/ca.pem - EMQX_AUTH__MONGO__SSL__CERTFILE=/emqx/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/client-cert.pem - EMQX_AUTH__MONGO__SSL__KEYFILE=/emqx/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/client-key.pem - EMQX_AUTH__MONGO__SSL__VERIFY=true - EMQX_AUTH__MONGO__SSL__SERVER_NAME_INDICATION=disable - EOF - - name: setup - env: - MONGO_TAG: ${{ matrix.mongo_tag }} - if: matrix.connect_type == 'tcp' - run: | - echo EMQX_AUTH__MONGO__SSL__ENABLE=off >> "$GITHUB_ENV" - - name: setup - if: matrix.network_type == 'ipv4' - run: | - echo "EMQX_AUTH__MONGO__SERVER=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' mongo):27017" >> "$GITHUB_ENV" - - name: setup - if: matrix.network_type == 'ipv6' - run: | - echo "EMQX_AUTH__MONGO__SERVER=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.GlobalIPv6Address}}{{end}}' mongo):27017" >> "$GITHUB_ENV" - - name: set git token - run: | - if make emqx-ee --dry-run > /dev/null 2>&1; then - docker exec -i erlang bash -c "echo \"https://ci%40emqx.io:${{ secrets.CI_GIT_TOKEN }}@github.com\" > /root/.git-credentials && git config --global credential.helper store" - fi - - name: run test cases - run: | - export CUTTLEFISH_ENV_OVERRIDE_PREFIX=EMQX_ - export HOCON_ENV_OVERRIDE_PREFIX=EMQX_ - printenv > .env - docker exec -i erlang sh -c "make ensure-rebar3" - docker exec -i erlang sh -c "./rebar3 eunit --application=emqx_auth_mongo" - docker exec --env-file .env -i erlang sh -c "make apps/emqx_auth_mongo-ct" - - uses: actions/upload-artifact@v1 - if: failure() - with: - name: logs_mongo${{ matrix.mongo_tag }}_${{ matrix.network_type }}_${{ matrix.connect_type }} - path: _build/test/logs - - mysql: - runs-on: ubuntu-20.04 - - strategy: - fail-fast: false - matrix: - mysql_tag: - - 5.7 - - 8 - network_type: - - ipv4 - - ipv6 - connect_type: - - tls - - tcp - - steps: - - uses: actions/checkout@v1 - - name: docker-compose up - timeout-minutes: 5 - run: | - docker-compose \ - -f .ci/docker-compose-file/docker-compose-mysql-${{ matrix.connect_type }}.yaml \ - -f .ci/docker-compose-file/docker-compose.yaml \ - up -d --build - while [ $(docker ps -a --filter name=client --filter exited=0 | wc -l) \ - != $(docker ps -a --filter name=client | wc -l) ]; do - sleep 5 - done - - name: setup - env: - MYSQL_TAG: ${{ matrix.mysql_tag }} - if: matrix.connect_type == 'tls' - run: | - cat <<-EOF >> "$GITHUB_ENV" - EMQX_AUTH__MYSQL__SSL__ENABLE=on - EMQX_AUTH__MYSQL__USERNAME=ssluser - EMQX_AUTH__MYSQL__PASSWORD=public - EMQX_AUTH__MYSQL__DATABASE=mqtt - EMQX_AUTH__MYSQL__SSL__CACERTFILE=/emqx/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/ca.pem - EMQX_AUTH__MYSQL__SSL__CERTFILE=/emqx/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/client-cert.pem - EMQX_AUTH__MYSQL__SSL__KEYFILE=/emqx/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/client-key.pem - EMQX_AUTH__MYSQL__SSL__VERIFY=true - EMQX_AUTH__MYSQL__SSL__SERVER_NAME_INDICATION=disable - EOF - - name: setup - env: - MYSQL_TAG: ${{ matrix.mysql_tag }} - if: matrix.connect_type == 'tcp' - run: | - cat <<-EOF >> "$GITHUB_ENV" - EMQX_AUTH__MYSQL__USERNAME=root - EMQX_AUTH__MYSQL__PASSWORD=public - EMQX_AUTH__MYSQL__DATABASE=mqtt - EMQX_AUTH__MYSQL__SSL__ENABLE=off - EOF - - name: setup - if: matrix.network_type == 'ipv4' - run: | - echo "EMQX_AUTH__MYSQL__SERVER=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' mysql):3306" >> "$GITHUB_ENV" - - name: setup - if: matrix.network_type == 'ipv6' - run: | - echo "EMQX_AUTH__MYSQL__SERVER=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.GlobalIPv6Address}}{{end}}' mysql):3306" >> "$GITHUB_ENV" - - name: set git token - run: | - if make emqx-ee --dry-run > /dev/null 2>&1; then - docker exec -i erlang bash -c "echo \"https://ci%40emqx.io:${{ secrets.CI_GIT_TOKEN }}@github.com\" > /root/.git-credentials && git config --global credential.helper store" - fi - - name: run test cases - run: | - export CUTTLEFISH_ENV_OVERRIDE_PREFIX=EMQX_ - export HOCON_ENV_OVERRIDE_PREFIX=EMQX_ - printenv > .env - docker exec -i erlang sh -c "make ensure-rebar3" - docker exec -i erlang sh -c "./rebar3 eunit --application=emqx_auth_mysql" - docker exec --env-file .env -i erlang sh -c "make apps/emqx_auth_mysql-ct" - - uses: actions/upload-artifact@v1 - if: failure() - with: - name: logs_mysql${{ matrix.mysql_tag }}_${{ matrix.network_type }}_${{ matrix.connect_type }} - path: _build/test/logs - - pgsql: - runs-on: ubuntu-20.04 - - strategy: - fail-fast: false - matrix: - pgsql_tag: - - 9 - - 10 - - 11 - - 12 - - 13 - network_type: - - ipv4 - - ipv6 - connect_type: - - tls - - tcp - steps: - - uses: actions/checkout@v1 - - name: docker-compose up - run: | - docker-compose \ - -f .ci/docker-compose-file/docker-compose-pgsql-${{ matrix.connect_type }}.yaml \ - -f .ci/docker-compose-file/docker-compose.yaml \ - up -d --build - - name: setup - env: - PGSQL_TAG: ${{ matrix.pgsql_tag }} - if: matrix.connect_type == 'tls' - run: | - cat <<-EOF >> "$GITHUB_ENV" - EMQX_AUTH__PGSQL__SSL__ENABLE=on - EMQX_AUTH__PGSQL__SSL__CACERTFILE=/emqx/apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data/ca.pem - EMQX_AUTH__PGSQL__SSL__CERTFILE=/emqx/apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data/client-cert.pem - EMQX_AUTH__PGSQL__SSL__KEYFILE=/emqx/apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data/client-key.pem - EMQX_AUTH__PGSQL__SSL__VERIFY=true - EMQX_AUTH__PGSQL__SSL__SERVER_NAME_INDICATION=disable - EOF - - name: setup - env: - PGSQL_TAG: ${{ matrix.pgsql_tag }} - if: matrix.connect_type == 'tcp' - run: | - echo EMQX_AUTH__PGSQL__SSL__ENABLE=off >> "$GITHUB_ENV" - - name: setup - if: matrix.network_type == 'ipv4' - run: | - echo "EMQX_AUTH__PGSQL__SERVER=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' pgsql):5432" >> "$GITHUB_ENV" - - name: setup - if: matrix.network_type == 'ipv6' - run: | - echo "EMQX_AUTH__PGSQL__SERVER=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.GlobalIPv6Address}}{{end}}' pgsql):5432" >> "$GITHUB_ENV" - - name: set git token - run: | - if make emqx-ee --dry-run > /dev/null 2>&1; then - docker exec -i erlang bash -c "echo \"https://ci%40emqx.io:${{ secrets.CI_GIT_TOKEN }}@github.com\" > /root/.git-credentials && git config --global credential.helper store" - fi - - name: run test cases - run: | - export EMQX_AUTH__PGSQL__USERNAME=root \ - EMQX_AUTH__PGSQL__PASSWORD=public \ - EMQX_AUTH__PGSQL__DATABASE=mqtt \ - CUTTLEFISH_ENV_OVERRIDE_PREFIX=EMQX_ \ - HOCON_ENV_OVERRIDE_PREFIX=EMQX_ - printenv > .env - docker exec -i erlang sh -c "make ensure-rebar3" - docker exec -i erlang sh -c "./rebar3 eunit --application=emqx_auth_pgsql" - docker exec --env-file .env -i erlang sh -c "make apps/emqx_auth_pgsql-ct" - - uses: actions/upload-artifact@v1 - if: failure() - with: - name: logs_pgsql${{ matrix.pgsql_tag }}_${{ matrix.network_type }}_${{ matrix.connect_type }} - path: _build/test/logs - - redis: - runs-on: ubuntu-20.04 - - strategy: - fail-fast: false - matrix: - redis_tag: - - 5 - - 6 - network_type: - - ipv4 - - ipv6 - connect_type: - - tls - - tcp - node_type: - - single - - sentinel - - cluster - exclude: - - redis_tag: 5 - connect_type: tls - - steps: - - uses: actions/checkout@v1 - - name: docker-compose up - run: | - docker-compose \ - -f .ci/docker-compose-file/docker-compose-redis-${{ matrix.node_type }}-${{ matrix.connect_type }}.yaml \ - -f .ci/docker-compose-file/docker-compose.yaml \ - up -d --build - - name: setup - env: - REDIS_TAG: ${{ matrix.redis_tag }} - if: matrix.connect_type == 'tls' - run: | - cat <<-EOF >> "$GITHUB_ENV" - EMQX_AUTH__REDIS__SSL__ENABLE=on - EMQX_AUTH__REDIS__SSL__CACERTFILE=/emqx/apps/emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/ca.crt - EMQX_AUTH__REDIS__SSL__CERTFILE=/emqx/apps/emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/redis.crt - EMQX_AUTH__REDIS__SSL__KEYFILE=/emqx/apps/emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/redis.key - EMQX_AUTH__REDIS__SSL__VERIFY=true - EMQX_AUTH__REDIS__SSL__SERVER_NAME_INDICATION=disable - EOF - - name: setup - env: - REDIS_TAG: ${{ matrix.redis_tag }} - if: matrix.connect_type == 'tcp' - run: | - echo EMQX_AUTH__REDIS__SSL__ENABLE=off >> "$GITHUB_ENV" - - name: get server address - run: | - ipv4_address=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' redis) - ipv6_address=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.GlobalIPv6Address}}{{end}}' redis) - cat <<-EOF >> "$GITHUB_ENV" - redis_ipv4_address=$ipv4_address - redis_ipv6_address=$ipv6_address - EOF - - name: setup - if: matrix.node_type == 'single' && matrix.connect_type == 'tcp' - run: | - cat <<-EOF >> "$GITHUB_ENV" - EMQX_AUTH__REDIS__TYPE=single - EMQX_AUTH__REDIS__SERVER=${redis_${{ matrix.network_type }}_address}:6379 - EOF - - name: setup - if: matrix.node_type == 'single' && matrix.connect_type == 'tls' - run: | - cat <<-EOF >> "$GITHUB_ENV" - EMQX_AUTH__REDIS__TYPE=single - EMQX_AUTH__REDIS__SERVER=${redis_${{ matrix.network_type }}_address}:6380 - EOF - - name: setup - if: matrix.node_type == 'sentinel' && matrix.connect_type == 'tcp' - run: | - cat <<-EOF >> "$GITHUB_ENV" - EMQX_AUTH__REDIS__TYPE=sentinel - EMQX_AUTH__REDIS__SERVER=${redis_${{ matrix.network_type }}_address}:26379 - EMQX_AUTH__REDIS__SENTINEL=mymaster - EOF - - name: setup - if: matrix.node_type == 'sentinel' && matrix.connect_type == 'tls' - run: | - cat <<-EOF >> "$GITHUB_ENV" - EMQX_AUTH__REDIS__TYPE=sentinel - EMQX_AUTH__REDIS__SERVER=${redis_${{ matrix.network_type }}_address}:26380 - EMQX_AUTH__REDIS__SENTINEL=mymaster - EOF - - name: setup - if: matrix.node_type == 'cluster' && matrix.connect_type == 'tcp' - run: | - cat <<-EOF >> "$GITHUB_ENV" - EMQX_AUTH__REDIS__TYPE=cluster - EMQX_AUTH__REDIS__SERVER=${redis_${{ matrix.network_type }}_address}:7000 - EOF - - name: setup - if: matrix.node_type == 'cluster' && matrix.connect_type == 'tls' - run: | - cat <<-EOF >> "$GITHUB_ENV" - EMQX_AUTH__REDIS__TYPE=cluster - EMQX_AUTH__REDIS__SERVER=${redis_${{ matrix.network_type }}_address}:8000 - EOF - - name: set git token - run: | - if make emqx-ee --dry-run > /dev/null 2>&1; then - docker exec -i erlang bash -c "echo \"https://ci%40emqx.io:${{ secrets.CI_GIT_TOKEN }}@github.com\" > /root/.git-credentials && git config --global credential.helper store" - fi - - name: run test cases - run: | - export CUTTLEFISH_ENV_OVERRIDE_PREFIX=EMQX_ - export EMQX_AUTH__REIDS__PASSWORD=public - printenv > .env - docker exec -i erlang sh -c "make ensure-rebar3" - docker exec -i erlang sh -c "./rebar3 eunit --application=emqx_auth_redis" - docker exec --env-file .env -i erlang sh -c "make apps/emqx_auth_redis-ct" - - uses: actions/upload-artifact@v1 - if: failure() - with: - name: logs_redis${{ matrix.redis_tag }}_${{ matrix.node_type }}_${{ matrix.network_type }}_${{ matrix.connect_type }} - path: _build/test/logs From c79e478c42bf16a575c578325cd6164d0cf868e1 Mon Sep 17 00:00:00 2001 From: Rory Z Date: Fri, 25 Jun 2021 17:36:32 +0800 Subject: [PATCH 033/174] chore: delete internal acl code --- apps/emqx/src/emqx_access_rule.erl | 152 ------------------ apps/emqx/src/emqx_schema.erl | 3 +- apps/emqx/test/emqx_SUITE_data/loaded_modules | 3 +- .../emqx/test/emqx_access_SUITE_data/acl.conf | 15 -- .../acl_deny_action.conf | 3 - apps/emqx/test/emqx_access_rule_SUITE.erl | 97 ----------- apps/emqx_coap/test/emqx_coap_SUITE.erl | 23 ++- .../src/emqx_mod_acl_internal.erl | 122 -------------- apps/emqx_modules/src/emqx_modules.erl | 18 --- .../test/emqx_mod_acl_internal_SUITE.erl | 64 -------- apps/emqx_modules/test/emqx_modules_SUITE.erl | 3 +- deploy/charts/emqx/values.yaml | 1 - deploy/docker/README.md | 3 +- 13 files changed, 19 insertions(+), 488 deletions(-) delete mode 100644 apps/emqx/src/emqx_access_rule.erl delete mode 100644 apps/emqx/test/emqx_access_SUITE_data/acl.conf delete mode 100644 apps/emqx/test/emqx_access_SUITE_data/acl_deny_action.conf delete mode 100644 apps/emqx/test/emqx_access_rule_SUITE.erl delete mode 100644 apps/emqx_modules/src/emqx_mod_acl_internal.erl delete mode 100644 apps/emqx_modules/test/emqx_mod_acl_internal_SUITE.erl diff --git a/apps/emqx/src/emqx_access_rule.erl b/apps/emqx/src/emqx_access_rule.erl deleted file mode 100644 index 5a607dd16..000000000 --- a/apps/emqx/src/emqx_access_rule.erl +++ /dev/null @@ -1,152 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2017-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_access_rule). - -%% APIs --export([ match/3 - , compile/1 - ]). - --export_type([rule/0]). - --type(acl_result() :: allow | deny). - --type(who() :: all | binary() | - {client, binary()} | - {user, binary()} | - {ipaddr, esockd_cidr:cidr_string()}). - --type(access() :: subscribe | publish | pubsub). - --type(rule() :: {acl_result(), all} | - {acl_result(), who(), access(), list(emqx_topic:topic())}). - --define(ALLOW_DENY(A), ((A =:= allow) orelse (A =:= deny))). --define(PUBSUB(A), ((A =:= subscribe) orelse (A =:= publish) orelse (A =:= pubsub))). - -%% @doc Compile Access Rule. -compile({A, all}) when ?ALLOW_DENY(A) -> - {A, all}; - -compile({A, Who, Access, Topic}) when ?ALLOW_DENY(A), ?PUBSUB(Access), is_binary(Topic) -> - {A, compile(who, Who), Access, [compile(topic, Topic)]}; - -compile({A, Who, Access, TopicFilters}) when ?ALLOW_DENY(A), ?PUBSUB(Access) -> - {A, compile(who, Who), Access, [compile(topic, Topic) || Topic <- TopicFilters]}. - -compile(who, all) -> - all; -compile(who, {ipaddr, CIDR}) -> - {ipaddr, esockd_cidr:parse(CIDR, true)}; -compile(who, {client, all}) -> - {client, all}; -compile(who, {client, ClientId}) -> - {client, bin(ClientId)}; -compile(who, {user, all}) -> - {user, all}; -compile(who, {user, Username}) -> - {user, bin(Username)}; -compile(who, {'and', Conds}) when is_list(Conds) -> - {'and', [compile(who, Cond) || Cond <- Conds]}; -compile(who, {'or', Conds}) when is_list(Conds) -> - {'or', [compile(who, Cond) || Cond <- Conds]}; - -compile(topic, {eq, Topic}) -> - {eq, emqx_topic:words(bin(Topic))}; -compile(topic, Topic) -> - Words = emqx_topic:words(bin(Topic)), - case pattern(Words) of - true -> {pattern, Words}; - false -> Words - end. - -pattern(Words) -> - lists:member(<<"%u">>, Words) orelse lists:member(<<"%c">>, Words). - -bin(L) when is_list(L) -> - list_to_binary(L); -bin(B) when is_binary(B) -> - B. - -%% @doc Match access rule --spec(match(emqx_types:clientinfo(), emqx_types:topic(), rule()) - -> {matched, allow} | {matched, deny} | nomatch). -match(_ClientInfo, _Topic, {AllowDeny, all}) when ?ALLOW_DENY(AllowDeny) -> - {matched, AllowDeny}; -match(ClientInfo, Topic, {AllowDeny, Who, _PubSub, TopicFilters}) - when ?ALLOW_DENY(AllowDeny) -> - case match_who(ClientInfo, Who) - andalso match_topics(ClientInfo, Topic, TopicFilters) of - true -> {matched, AllowDeny}; - false -> nomatch - end. - -match_who(_ClientInfo, all) -> - true; -match_who(_ClientInfo, {user, all}) -> - true; -match_who(_ClientInfo, {client, all}) -> - true; -match_who(#{clientid := ClientId}, {client, ClientId}) -> - true; -match_who(#{username := Username}, {user, Username}) -> - true; -match_who(#{peerhost := undefined}, {ipaddr, _Tup}) -> - false; -match_who(#{peerhost := IP}, {ipaddr, CIDR}) -> - esockd_cidr:match(IP, CIDR); -match_who(ClientInfo, {'and', Conds}) when is_list(Conds) -> - lists:foldl(fun(Who, Allow) -> - match_who(ClientInfo, Who) andalso Allow - end, true, Conds); -match_who(ClientInfo, {'or', Conds}) when is_list(Conds) -> - lists:foldl(fun(Who, Allow) -> - match_who(ClientInfo, Who) orelse Allow - end, false, Conds); -match_who(_ClientInfo, _Who) -> - false. - -match_topics(_ClientInfo, _Topic, []) -> - false; -match_topics(ClientInfo, Topic, [{pattern, PatternFilter}|Filters]) -> - TopicFilter = feed_var(ClientInfo, PatternFilter), - match_topic(emqx_topic:words(Topic), TopicFilter) - orelse match_topics(ClientInfo, Topic, Filters); -match_topics(ClientInfo, Topic, [TopicFilter|Filters]) -> - match_topic(emqx_topic:words(Topic), TopicFilter) - orelse match_topics(ClientInfo, Topic, Filters). - -match_topic(Topic, {eq, TopicFilter}) -> - Topic == TopicFilter; -match_topic(Topic, TopicFilter) -> - emqx_topic:match(Topic, TopicFilter). - -feed_var(ClientInfo, Pattern) -> - feed_var(ClientInfo, Pattern, []). -feed_var(_ClientInfo, [], Acc) -> - lists:reverse(Acc); -feed_var(ClientInfo = #{clientid := undefined}, [<<"%c">>|Words], Acc) -> - feed_var(ClientInfo, Words, [<<"%c">>|Acc]); -feed_var(ClientInfo = #{clientid := ClientId}, [<<"%c">>|Words], Acc) -> - feed_var(ClientInfo, Words, [ClientId |Acc]); -feed_var(ClientInfo = #{username := undefined}, [<<"%u">>|Words], Acc) -> - feed_var(ClientInfo, Words, [<<"%u">>|Acc]); -feed_var(ClientInfo = #{username := Username}, [<<"%u">>|Words], Acc) -> - feed_var(ClientInfo, Words, [Username|Acc]); -feed_var(ClientInfo, [W|Words], Acc) -> - feed_var(ClientInfo, Words, [W|Acc]). - diff --git a/apps/emqx/src/emqx_schema.erl b/apps/emqx/src/emqx_schema.erl index 8c1967ed3..83e611ee0 100644 --- a/apps/emqx/src/emqx_schema.erl +++ b/apps/emqx/src/emqx_schema.erl @@ -823,8 +823,7 @@ tr_modules(Conf) -> [{emqx_mod_subscription, Subscriptions()}], [{emqx_mod_rewrite, Rewrites()}], [{emqx_mod_topic_metrics, []}], - [{emqx_mod_delayed, []}], - [{emqx_mod_acl_internal, [{acl_file, conf_get("acl.acl_file", Conf)}]}] + [{emqx_mod_delayed, []}] ]). tr_sysmon(Conf) -> diff --git a/apps/emqx/test/emqx_SUITE_data/loaded_modules b/apps/emqx/test/emqx_SUITE_data/loaded_modules index 49effa0c5..f31e47900 100644 --- a/apps/emqx/test/emqx_SUITE_data/loaded_modules +++ b/apps/emqx/test/emqx_SUITE_data/loaded_modules @@ -1,2 +1 @@ -{emqx_mod_acl_internal, true}. -{emqx_mod_presence, true}. \ No newline at end of file +{emqx_mod_presence, true}. diff --git a/apps/emqx/test/emqx_access_SUITE_data/acl.conf b/apps/emqx/test/emqx_access_SUITE_data/acl.conf deleted file mode 100644 index e5730b4c5..000000000 --- a/apps/emqx/test/emqx_access_SUITE_data/acl.conf +++ /dev/null @@ -1,15 +0,0 @@ -{allow, {ipaddr, "127.0.0.1"}, subscribe, ["$SYS/#", "#"]}. - -{allow, {user, "testuser"}, subscribe, ["a/b/c", "d/e/f/#"]}. - -{allow, {user, "admin"}, pubsub, ["a/b/c", "d/e/f/#"]}. - -{allow, {client, "testClient"}, subscribe, ["testTopics/testClient"]}. - -{allow, all, subscribe, ["clients/%c"]}. - -{allow, all, pubsub, ["users/%u/#"]}. - -{deny, all, subscribe, ["$SYS/#", "#"]}. - -{deny, all}. diff --git a/apps/emqx/test/emqx_access_SUITE_data/acl_deny_action.conf b/apps/emqx/test/emqx_access_SUITE_data/acl_deny_action.conf deleted file mode 100644 index c7f933ce7..000000000 --- a/apps/emqx/test/emqx_access_SUITE_data/acl_deny_action.conf +++ /dev/null @@ -1,3 +0,0 @@ -{deny, {user, "emqx"}, pubsub, ["acl_deny_action"]}. -{deny, {user, "pub_deny"}, publish, ["pub_deny"]}. -{allow, all}. \ No newline at end of file diff --git a/apps/emqx/test/emqx_access_rule_SUITE.erl b/apps/emqx/test/emqx_access_rule_SUITE.erl deleted file mode 100644 index 93c84a958..000000000 --- a/apps/emqx/test/emqx_access_rule_SUITE.erl +++ /dev/null @@ -1,97 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2019-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_access_rule_SUITE). - --compile(export_all). --compile(nowarn_export_all). - --include_lib("eunit/include/eunit.hrl"). - -all() -> emqx_ct:all(?MODULE). - -init_per_suite(Config) -> - emqx_ct_helpers:boot_modules([router, broker]), - emqx_ct_helpers:start_apps([]), - Config. - -end_per_suite(_Config) -> - emqx_ct_helpers:stop_apps([]). - -t_compile(_) -> - Rule1 = {allow, all, pubsub, <<"%u">>}, - Compile1 = {allow, all, pubsub, [{pattern,[<<"%u">>]}]}, - - Rule2 = {allow, {ipaddr, "127.0.0.1"}, pubsub, <<"%c">>}, - Compile2 = {allow, {ipaddr, {{127,0,0,1}, {127,0,0,1}, 32}}, pubsub, [{pattern,[<<"%c">>]}]}, - - Rule3 = {allow, {'and', [{client, <<"testClient">>}, {user, <<"testUser">>}]}, pubsub, [<<"testTopics1">>, <<"testTopics2">>]}, - Compile3 = {allow, {'and', [{client, <<"testClient">>}, {user, <<"testUser">>}]}, pubsub, [[<<"testTopics1">>], [<<"testTopics2">>]]}, - - Rule4 = {allow, {'or', [{client, all}, {user, all}]}, pubsub, [ <<"testTopics1">>, <<"testTopics2">>]}, - Compile4 = {allow, {'or', [{client, all}, {user, all}]}, pubsub, [[<<"testTopics1">>], [<<"testTopics2">>]]}, - - ?assertEqual(Compile1, emqx_access_rule:compile(Rule1)), - ?assertEqual(Compile2, emqx_access_rule:compile(Rule2)), - ?assertEqual(Compile3, emqx_access_rule:compile(Rule3)), - ?assertEqual(Compile4, emqx_access_rule:compile(Rule4)). - -t_match(_) -> - ClientInfo1 = #{zone => external, - clientid => <<"testClient">>, - username => <<"TestUser">>, - peerhost => {127,0,0,1} - }, - ClientInfo2 = #{zone => external, - clientid => <<"testClient">>, - username => <<"TestUser">>, - peerhost => {192,168,0,10} - }, - ClientInfo3 = #{zone => external, - clientid => <<"testClient">>, - username => <<"TestUser">>, - peerhost => undefined - }, - ?assertEqual({matched, deny}, emqx_access_rule:match([], [], {deny, all})), - ?assertEqual({matched, allow}, emqx_access_rule:match([], [], {allow, all})), - ?assertEqual(nomatch, emqx_access_rule:match(ClientInfo1, <<"Test/Topic">>, - emqx_access_rule:compile({allow, {user, all}, pubsub, []}))), - ?assertEqual({matched, allow}, emqx_access_rule:match(ClientInfo1, <<"Test/Topic">>, - emqx_access_rule:compile({allow, {client, all}, pubsub, ["$SYS/#", "#"]}))), - ?assertEqual(nomatch, emqx_access_rule:match(ClientInfo3, <<"Test/Topic">>, - emqx_access_rule:compile({allow, {ipaddr, "127.0.0.1"}, pubsub, ["$SYS/#", "#"]}))), - ?assertEqual({matched, allow}, emqx_access_rule:match(ClientInfo1, <<"Test/Topic">>, - emqx_access_rule:compile({allow, {ipaddr, "127.0.0.1"}, subscribe, ["$SYS/#", "#"]}))), - ?assertEqual({matched, allow}, emqx_access_rule:match(ClientInfo2, <<"Test/Topic">>, - emqx_access_rule:compile({allow, {ipaddr, "192.168.0.1/24"}, subscribe, ["$SYS/#", "#"]}))), - ?assertEqual({matched, allow}, emqx_access_rule:match(ClientInfo1, <<"d/e/f/x">>, - emqx_access_rule:compile({allow, {user, "TestUser"}, subscribe, ["a/b/c", "d/e/f/#"]}))), - ?assertEqual(nomatch, emqx_access_rule:match(ClientInfo1, <<"d/e/f/x">>, - emqx_access_rule:compile({allow, {user, "admin"}, pubsub, ["d/e/f/#"]}))), - ?assertEqual({matched, allow}, emqx_access_rule:match(ClientInfo1, <<"testTopics/testClient">>, - emqx_access_rule:compile({allow, {client, "testClient"}, publish, ["testTopics/testClient"]}))), - ?assertEqual({matched, allow}, emqx_access_rule:match(ClientInfo1, <<"clients/testClient">>, - emqx_access_rule:compile({allow, all, pubsub, ["clients/%c"]}))), - ?assertEqual({matched, allow}, emqx_access_rule:match(#{username => <<"user2">>}, <<"users/user2/abc/def">>, - emqx_access_rule:compile({allow, all, subscribe, ["users/%u/#"]}))), - ?assertEqual({matched, deny}, emqx_access_rule:match(ClientInfo1, <<"d/e/f">>, - emqx_access_rule:compile({deny, all, subscribe, ["$SYS/#", "#"]}))), - ?assertEqual(nomatch, emqx_access_rule:match(ClientInfo1, <<"Topic">>, - emqx_access_rule:compile({allow, {'and', [{ipaddr, "127.0.0.1"}, {user, <<"WrongUser">>}]}, publish, <<"Topic">>}))), - ?assertEqual({matched, allow}, emqx_access_rule:match(ClientInfo1, <<"Topic">>, - emqx_access_rule:compile({allow, {'and', [{ipaddr, "127.0.0.1"}, {user, <<"TestUser">>}]}, publish, <<"Topic">>}))), - ?assertEqual({matched, allow}, emqx_access_rule:match(ClientInfo1, <<"Topic">>, - emqx_access_rule:compile({allow, {'or', [{ipaddr, "127.0.0.1"}, {user, <<"WrongUser">>}]}, publish, ["Topic"]}))). diff --git a/apps/emqx_coap/test/emqx_coap_SUITE.erl b/apps/emqx_coap/test/emqx_coap_SUITE.erl index 440b80ebd..73c9ef162 100644 --- a/apps/emqx_coap/test/emqx_coap_SUITE.erl +++ b/apps/emqx_coap/test/emqx_coap_SUITE.erl @@ -266,11 +266,18 @@ t_kick_1(_Config) -> % mqtt connection kicked by coap with same client id t_acl(Config) -> - %% Update acl file and reload mod_acl_internal - Path = filename:join([testdir(proplists:get_value(data_dir, Config)), "deny.conf"]), - ok = file:write_file(Path, <<"{deny, {user, \"coap\"}, publish, [\"abc\"]}.">>), - OldPath = emqx:get_env(acl_file), - emqx_mod_acl_internal:reload([{acl_file, Path}]), + OldPath = emqx:get_env(plugins_etc_dir), + application:set_env(emqx, plugins_etc_dir, + emqx_ct_helpers:deps_path(emqx_authz, "test")), + Conf = #{<<"authz">> => + #{<<"rules">> => + [#{<<"principal">> =>#{<<"username">> => <<"coap">>}, + <<"permission">> => deny, + <<"topics">> => [<<"abc">>], + <<"action">> => <<"publish">>} + ]}}, + ok = file:write_file(filename:join(emqx:get_env(plugins_etc_dir), 'authz.conf'), jsx:encode(Conf)), + application:ensure_all_started(emqx_authz), emqx:subscribe(<<"abc">>), URI = "coap://127.0.0.1/mqtt/adbc?c=client1&u=coap&p=secret", @@ -282,9 +289,9 @@ t_acl(Config) -> ok end, - application:set_env(emqx, acl_file, OldPath), - file:delete(Path), - emqx_mod_acl_internal:reload([{acl_file, OldPath}]). + file:delete(filename:join(emqx:get_env(plugins_etc_dir), 'authz.conf')), + application:set_env(emqx, plugins_etc_dir, OldPath), + application:stop(emqx_authz). t_stats(_) -> ok. diff --git a/apps/emqx_modules/src/emqx_mod_acl_internal.erl b/apps/emqx_modules/src/emqx_mod_acl_internal.erl deleted file mode 100644 index 8956229ea..000000000 --- a/apps/emqx_modules/src/emqx_mod_acl_internal.erl +++ /dev/null @@ -1,122 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_mod_acl_internal). - --behaviour(emqx_gen_mod). - --include_lib("emqx/include/emqx.hrl"). --include_lib("emqx/include/logger.hrl"). - --logger_header("[ACL_INTERNAL]"). - -%% APIs --export([ check_acl/5 - , rules_from_file/1 - ]). - -%% emqx_gen_mod callbacks --export([ load/1 - , unload/1 - , reload/1 - , description/0 - ]). - --type(acl_rules() :: #{publish => [emqx_access_rule:rule()], - subscribe => [emqx_access_rule:rule()]}). - -%%-------------------------------------------------------------------- -%% API -%%-------------------------------------------------------------------- - -load(Env) -> - Rules = rules_from_file(proplists:get_value(acl_file, Env)), - emqx_hooks:add('client.check_acl', {?MODULE, check_acl, [Rules]}, -1). - -unload(_Env) -> - emqx_hooks:del('client.check_acl', {?MODULE, check_acl}). - -reload(Env) -> - emqx_acl_cache:is_enabled() andalso ( - lists:foreach( - fun(Pid) -> erlang:send(Pid, clean_acl_cache) end, - emqx_cm:all_channels())), - unload(Env), load(Env). - -description() -> - "EMQ X Internal ACL Module". -%%-------------------------------------------------------------------- -%% ACL callbacks -%%-------------------------------------------------------------------- - -%% @doc Check ACL --spec(check_acl(emqx_types:clientinfo(), emqx_types:pubsub(), emqx_topic:topic(), - emqx_access_rule:acl_result(), acl_rules()) - -> {ok, allow} | {ok, deny} | ok). -check_acl(Client, PubSub, Topic, _AclResult, Rules) -> - case match(Client, Topic, lookup(PubSub, Rules)) of - {matched, allow} -> {ok, allow}; - {matched, deny} -> {ok, deny}; - nomatch -> ok - end. - -%%-------------------------------------------------------------------- -%% Internal Functions -%%-------------------------------------------------------------------- -lookup(PubSub, Rules) -> - maps:get(PubSub, Rules, []). - -match(_Client, _Topic, []) -> - nomatch; -match(Client, Topic, [Rule|Rules]) -> - case emqx_access_rule:match(Client, Topic, Rule) of - nomatch -> - match(Client, Topic, Rules); - {matched, AllowDeny} -> - {matched, AllowDeny} - end. - --spec(rules_from_file(file:filename()) -> map()). -rules_from_file(AclFile) -> - case file:consult(AclFile) of - {ok, Terms} -> - Rules = [emqx_access_rule:compile(Term) || Term <- Terms], - #{publish => [Rule || Rule <- Rules, filter(publish, Rule)], - subscribe => [Rule || Rule <- Rules, filter(subscribe, Rule)]}; - {error, eacces} -> - ?LOG(alert, "Insufficient permissions to read the ~s file", [AclFile]), - #{}; - {error, enoent} -> - ?LOG(alert, "The ~s file does not exist", [AclFile]), - #{}; - {error, Reason} -> - ?LOG(alert, "Failed to read ~s: ~p", [AclFile, Reason]), - #{} - end. - -filter(_PubSub, {allow, all}) -> - true; -filter(_PubSub, {deny, all}) -> - true; -filter(publish, {_AllowDeny, _Who, publish, _Topics}) -> - true; -filter(_PubSub, {_AllowDeny, _Who, pubsub, _Topics}) -> - true; -filter(subscribe, {_AllowDeny, _Who, subscribe, _Topics}) -> - true; -filter(_PubSub, {_AllowDeny, _Who, _, _Topics}) -> - false. - diff --git a/apps/emqx_modules/src/emqx_modules.erl b/apps/emqx_modules/src/emqx_modules.erl index 3de9c6ba3..262e700ab 100644 --- a/apps/emqx_modules/src/emqx_modules.erl +++ b/apps/emqx_modules/src/emqx_modules.erl @@ -80,17 +80,6 @@ unload(ModuleName) -> end. -spec(reload(module()) -> ok | ignore | {error, any()}). -reload(emqx_mod_acl_internal) -> - Modules = emqx:get_env(modules, []), - Env = proplists:get_value(emqx_mod_acl_internal, Modules, undefined), - case emqx_mod_acl_internal:reload(Env) of - ok -> - ?LOG(info, "Reload ~s module successfully.", [emqx_mod_acl_internal]), - ok; - {error, Error} -> - ?LOG(error, "Reload module ~s failed, cannot start for ~0p", [emqx_mod_acl_internal, Error]), - {error, Error} - end; reload(_) -> ignore. @@ -197,13 +186,6 @@ cli(["unload", Name]) -> emqx_ctl:print("Unload module ~s error: ~p.~n", [Name, Reason]) end; -cli(["reload", "emqx_mod_acl_internal" = Name]) -> - case emqx_modules:reload(list_to_atom(Name)) of - ok -> - emqx_ctl:print("Module ~s reloaded successfully.~n", [Name]); - {error, Reason} -> - emqx_ctl:print("Reload module ~s error: ~p.~n", [Name, Reason]) - end; cli(["reload", Name]) -> emqx_ctl:print("Module: ~p does not need to be reloaded.~n", [Name]); diff --git a/apps/emqx_modules/test/emqx_mod_acl_internal_SUITE.erl b/apps/emqx_modules/test/emqx_mod_acl_internal_SUITE.erl deleted file mode 100644 index 3edcd31fa..000000000 --- a/apps/emqx_modules/test/emqx_mod_acl_internal_SUITE.erl +++ /dev/null @@ -1,64 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_mod_acl_internal_SUITE). - --compile(export_all). --compile(nowarn_export_all). - --include_lib("emqx/include/emqx_mqtt.hrl"). --include_lib("eunit/include/eunit.hrl"). - -all() -> emqx_ct:all(?MODULE). - -init_per_suite(Config) -> - emqx_ct_helpers:boot_modules(all), - emqx_ct_helpers:start_apps([]), - Config. - -end_per_suite(_Config) -> - emqx_ct_helpers:stop_apps([]). - -t_load_unload(_) -> - ?assertEqual(ok, emqx_mod_acl_internal:unload([])), - ?assertEqual(ok, emqx_mod_acl_internal:load([])), - ?assertEqual({error,already_exists}, emqx_mod_acl_internal:load([])). - -t_check_acl(_) -> - Rules=#{publish => [{allow,all}], subscribe => [{deny, all}]}, - ?assertEqual({ok, allow}, emqx_mod_acl_internal:check_acl(clientinfo(), publish, <<"t">>, [], Rules)), - ?assertEqual({ok, deny}, emqx_mod_acl_internal:check_acl(clientinfo(), subscribe, <<"t">>, [], Rules)), - ?assertEqual(ok, emqx_mod_acl_internal:check_acl(clientinfo(), connect, <<"t">>, [], Rules)). - -t_reload_acl(_) -> - ?assertEqual(ok, emqx_mod_acl_internal:reload([])). - -%%-------------------------------------------------------------------- -%% Helper functions -%%-------------------------------------------------------------------- - -clientinfo() -> clientinfo(#{}). -clientinfo(InitProps) -> - maps:merge(#{zone => zone, - protocol => mqtt, - peerhost => {127,0,0,1}, - clientid => <<"clientid">>, - username => <<"username">>, - password => <<"passwd">>, - is_superuser => false, - peercert => undefined, - mountpoint => undefined - }, InitProps). diff --git a/apps/emqx_modules/test/emqx_modules_SUITE.erl b/apps/emqx_modules/test/emqx_modules_SUITE.erl index dc76e8eb7..0ce8b0c5f 100644 --- a/apps/emqx_modules/test/emqx_modules_SUITE.erl +++ b/apps/emqx_modules/test/emqx_modules_SUITE.erl @@ -49,8 +49,7 @@ t_load(_) -> ?assertEqual(ok, emqx_modules:load()), ?assertEqual({error, not_found}, emqx_modules:load(not_existed_module)), ?assertEqual({error, not_started}, emqx_modules:unload(emqx_mod_rewrite)), - ?assertEqual(ignore, emqx_modules:reload(emqx_mod_rewrite)), - ?assertEqual(ok, emqx_modules:reload(emqx_mod_acl_internal)). + ?assertEqual(ignore, emqx_modules:reload(emqx_mod_rewrite)). t_list(_) -> ?assertMatch([{_, _} | _ ], emqx_modules:list()). diff --git a/deploy/charts/emqx/values.yaml b/deploy/charts/emqx/values.yaml index 0e6f24a66..3e145aafe 100644 --- a/deploy/charts/emqx/values.yaml +++ b/deploy/charts/emqx/values.yaml @@ -87,7 +87,6 @@ emqxLoadedPlugins: > {emqx_bridge_mqtt, false}. emqxLoadedModules: > - {emqx_mod_acl_internal, true}. {emqx_mod_presence, true}. {emqx_mod_delayed, false}. {emqx_mod_rewrite, false}. diff --git a/deploy/docker/README.md b/deploy/docker/README.md index f243c7a4b..4c89a73cd 100644 --- a/deploy/docker/README.md +++ b/deploy/docker/README.md @@ -97,12 +97,11 @@ For example, set mqtt tcp port to 1883 Default environment variable ``EMQX_LOADED_MODULES``, including -+ ``emqx_mod_acl_internal`` + ``emqx_mod_presence`` ```bash # The default EMQX_LOADED_MODULES env -EMQX_LOADED_MODULES="emqx_mod_acl_internal,emqx_mod_acl_internal" +EMQX_LOADED_MODULES="emqx_mod_presence" ``` For example, set ``EMQX_LOADED_MODULES=emqx_mod_delayed,emqx_mod_rewrite`` to load these two modules. From 6063fc72f71763a2640cd89cb9a42edfb7f280f0 Mon Sep 17 00:00:00 2001 From: k32 <10274441+k32@users.noreply.github.com> Date: Fri, 25 Jun 2021 16:27:33 +0200 Subject: [PATCH 034/174] chore(authentication): Migrate to RLOG --- apps/emqx_authentication/include/emqx_authentication.hrl | 2 ++ apps/emqx_authentication/src/emqx_authentication.erl | 9 ++++++--- apps/emqx_authentication/src/emqx_authentication_app.erl | 3 +++ .../src/emqx_authentication_mnesia.erl | 8 +++++--- .../test/emqx_authentication_SUITE.erl | 6 ++---- 5 files changed, 18 insertions(+), 10 deletions(-) diff --git a/apps/emqx_authentication/include/emqx_authentication.hrl b/apps/emqx_authentication/include/emqx_authentication.hrl index 09d3c5fc4..814c03eb4 100644 --- a/apps/emqx_authentication/include/emqx_authentication.hrl +++ b/apps/emqx_authentication/include/emqx_authentication.hrl @@ -39,3 +39,5 @@ , services :: [{service_name(), #service{}}] , created_at :: integer() }). + +-define(AUTH_SHARD, emqx_authentication_shard). diff --git a/apps/emqx_authentication/src/emqx_authentication.erl b/apps/emqx_authentication/src/emqx_authentication.erl index ab7b8537c..a3d0241c0 100644 --- a/apps/emqx_authentication/src/emqx_authentication.erl +++ b/apps/emqx_authentication/src/emqx_authentication.erl @@ -56,6 +56,9 @@ -define(CHAIN_TAB, emqx_authentication_chain). -define(SERVICE_TYPE_TAB, emqx_authentication_service_type). +-rlog_shard({?AUTH_SHARD, ?CHAIN_TAB}). +-rlog_shard({?AUTH_SHARD, ?SERVICE_TYPE_TAB}). + %%------------------------------------------------------------------------------ %% Mnesia bootstrap %%------------------------------------------------------------------------------ @@ -370,7 +373,7 @@ validate_other_service_params([#{type := Type, params := Params} = ServiceParams {error, not_found} -> {error, {not_found, {service_type, Type}}} end. - + no_duplicate_names(Names) -> no_duplicate_names(Names, #{}). @@ -423,7 +426,7 @@ extract_services([ServiceName | More], Services, Acc) -> false -> {error, {not_found, {service, ServiceName}}} end. - + move_service_to_the_front_(ServiceName, Services) -> move_service_to_the_front_(ServiceName, Services, []). @@ -513,7 +516,7 @@ trans(Fun) -> trans(Fun, []). trans(Fun, Args) -> - case mnesia:transaction(Fun, Args) of + case ekka_mnesia:transaction(?AUTH_SHARD, Fun, Args) of {atomic, Res} -> Res; {aborted, Reason} -> {error, Reason} end. diff --git a/apps/emqx_authentication/src/emqx_authentication_app.erl b/apps/emqx_authentication/src/emqx_authentication_app.erl index 2d395def7..3bea3c3d6 100644 --- a/apps/emqx_authentication/src/emqx_authentication_app.erl +++ b/apps/emqx_authentication/src/emqx_authentication_app.erl @@ -20,6 +20,8 @@ -emqx_plugin(?MODULE). +-include("emqx_authentication.hrl"). + %% Application callbacks -export([ start/2 , stop/1 @@ -27,6 +29,7 @@ start(_StartType, _StartArgs) -> {ok, Sup} = emqx_authentication_sup:start_link(), + ok = ekka_rlog:wait_for_shards([?AUTH_SHARD], infinity), ok = emqx_authentication:register_service_types(), {ok, Sup}. diff --git a/apps/emqx_authentication/src/emqx_authentication_mnesia.erl b/apps/emqx_authentication/src/emqx_authentication_mnesia.erl index 53dc4dd73..09307e38f 100644 --- a/apps/emqx_authentication/src/emqx_authentication_mnesia.erl +++ b/apps/emqx_authentication/src/emqx_authentication_mnesia.erl @@ -51,7 +51,7 @@ salt_rounds => #{ order => 3, type => number, - default => 10 + default => 10 } } }). @@ -72,6 +72,8 @@ -define(TAB, mnesia_basic_auth). +-rlog_shard({?AUTH_SHARD, ?TAB}). + %%------------------------------------------------------------------------------ %% Mnesia bootstrap %%------------------------------------------------------------------------------ @@ -231,7 +233,7 @@ import(UserGroup, [#{<<"user_id">> := UserID, import(_UserGroup, [_ | _More]) -> {error, bad_format}. -%% Importing 5w users needs 1.7 seconds +%% Importing 5w users needs 1.7 seconds import(UserGroup, File, Seq) -> case file:read_line(File) of {ok, Line} -> @@ -330,7 +332,7 @@ trans(Fun) -> trans(Fun, []). trans(Fun, Args) -> - case mnesia:transaction(Fun, Args) of + case ekka_mnesia:transaction(?AUTH_SHARD, Fun, Args) of {atomic, Res} -> Res; {aborted, Reason} -> {error, Reason} end. diff --git a/apps/emqx_authentication/test/emqx_authentication_SUITE.erl b/apps/emqx_authentication/test/emqx_authentication_SUITE.erl index d110d940a..d9f9ace8b 100644 --- a/apps/emqx_authentication/test/emqx_authentication_SUITE.erl +++ b/apps/emqx_authentication/test/emqx_authentication_SUITE.erl @@ -28,6 +28,7 @@ all() -> emqx_ct:all(?MODULE). init_per_suite(Config) -> + application:set_env(ekka, strict_mode, true), emqx_ct_helpers:start_apps([emqx_authentication]), Config. @@ -40,7 +41,7 @@ t_chain(_) -> ?assertMatch({ok, #{id := ChainID, services := []}}, ?AUTH:create_chain(#{id => ChainID})), ?assertEqual({error, {already_exists, {chain, ChainID}}}, ?AUTH:create_chain(#{id => ChainID})), ?assertMatch({ok, #{id := ChainID, services := []}}, ?AUTH:lookup_chain(ChainID)), - ?assertEqual(ok, ?AUTH:delete_chain(ChainID)), + ?assertEqual(ok, ?AUTH:delete_chain(ChainID)), ?assertMatch({error, {not_found, {chain, ChainID}}}, ?AUTH:lookup_chain(ChainID)), ok. @@ -186,6 +187,3 @@ t_multi_mnesia_service(_) -> ?assertEqual(ok, ?AUTH:authenticate(ClientInfo2)), ?assertEqual(ok, ?AUTH:delete_chain(ChainID)), ok. - - - From 1063f2cae5b48bceff1eb48c09516753c64a46a5 Mon Sep 17 00:00:00 2001 From: Turtle Date: Mon, 28 Jun 2021 09:24:57 +0800 Subject: [PATCH 035/174] chore(plugins): rm emqx-lua-hook plugin --- apps/emqx_lua_hook/.gitignore | 19 - apps/emqx_lua_hook/README.md | 338 --------- apps/emqx_lua_hook/etc/emqx_lua_hook.conf | 4 - apps/emqx_lua_hook/examples.lua | 71 -- apps/emqx_lua_hook/include/emqx_lua_hook.hrl | 18 - apps/emqx_lua_hook/priv/emqx_lua_hook.schema | 1 - apps/emqx_lua_hook/rebar.config | 21 - apps/emqx_lua_hook/src/emqx_lua_hook.app.src | 14 - apps/emqx_lua_hook/src/emqx_lua_hook.erl | 199 ----- apps/emqx_lua_hook/src/emqx_lua_hook_app.erl | 40 - apps/emqx_lua_hook/src/emqx_lua_hook_cli.erl | 88 --- apps/emqx_lua_hook/src/emqx_lua_hook_sup.erl | 35 - apps/emqx_lua_hook/src/emqx_lua_script.erl | 342 --------- .../test/emqx_lua_hook_SUITE.erl | 693 ------------------ rebar.config.erl | 4 +- 15 files changed, 1 insertion(+), 1886 deletions(-) delete mode 100644 apps/emqx_lua_hook/.gitignore delete mode 100644 apps/emqx_lua_hook/README.md delete mode 100644 apps/emqx_lua_hook/etc/emqx_lua_hook.conf delete mode 100644 apps/emqx_lua_hook/examples.lua delete mode 100644 apps/emqx_lua_hook/include/emqx_lua_hook.hrl delete mode 100644 apps/emqx_lua_hook/priv/emqx_lua_hook.schema delete mode 100644 apps/emqx_lua_hook/rebar.config delete mode 100644 apps/emqx_lua_hook/src/emqx_lua_hook.app.src delete mode 100644 apps/emqx_lua_hook/src/emqx_lua_hook.erl delete mode 100644 apps/emqx_lua_hook/src/emqx_lua_hook_app.erl delete mode 100644 apps/emqx_lua_hook/src/emqx_lua_hook_cli.erl delete mode 100644 apps/emqx_lua_hook/src/emqx_lua_hook_sup.erl delete mode 100644 apps/emqx_lua_hook/src/emqx_lua_script.erl delete mode 100644 apps/emqx_lua_hook/test/emqx_lua_hook_SUITE.erl diff --git a/apps/emqx_lua_hook/.gitignore b/apps/emqx_lua_hook/.gitignore deleted file mode 100644 index af616ea1a..000000000 --- a/apps/emqx_lua_hook/.gitignore +++ /dev/null @@ -1,19 +0,0 @@ -deps/ -ebin/ -_rel/ -.erlang.mk/ -*.d -data/ -*.iml -.idea/ -logs/ -*.beam -.DS_Store -erlang.mk -_build/ -rebar.lock -rebar3.crashdump -bbmustache/ -*.conf.rendered -.rebar3 -*.swp diff --git a/apps/emqx_lua_hook/README.md b/apps/emqx_lua_hook/README.md deleted file mode 100644 index a3f65a094..000000000 --- a/apps/emqx_lua_hook/README.md +++ /dev/null @@ -1,338 +0,0 @@ - -# emqx-lua-hook - -This plugin makes it possible to write hooks in lua scripts. - -Lua virtual machine is implemented by [luerl](https://github.com/rvirding/luerl) which supports Lua 5.2. Following features may not work properly: -* label and goto -* tail-call optimisation in return -* only limited standard libraries -* proper handling of `__metatable` - -For the supported functions, please refer to luerl's [project page](https://github.com/rvirding/luerl). - -Lua scripts are stored in 'data/scripts' directory, and will be loaded automatically. If a script is changed during runtime, it should be reloaded to take effect. - -Each lua script could export several functions binding with emqx hooks, triggered by message publish, topic subscribe, client connect, etc. Different lua scripts may export same type function, binding with a same event. But their order being triggered is not guaranteed. - -To start this plugin, run following command: - -```shell -bin/emqx_ctl plugins load emqx_lua_hook -``` - - -## NOTE - -* Since lua VM is run on erlang VM, its performance is poor. Please do NOT write long or complicated lua scripts which may degrade entire system. -* It's hard to debug lua script in emqx environment. Recommended to unit test your lua script in your host first. If everything is OK, deploy it to emqx 'data/scripts' directory. -* Global variable will lost its value for each call. Do NOT use global variable in lua scripts. - - -# Example - -Suppose your emqx is installed in /emqx, and the lua script directory should be /emqx/data/scripts. - -Make a new file called "test.lua" and put following code into this file: - -```lua -function on_message_publish(clientid, username, topic, payload, qos, retain) - return topic, "hello", qos, retain -end - -function register_hook() - return "on_message_publish" -end -``` - -Execute following command to start emq-lua-hook and load scripts in 'data/scripts' directory. - -``` -/emqx/bin/emqx_ctl plugins load emqx_lua_hook -``` - -Now let's take a look at what will happend. - -- Start a mqtt client, such as mqtt.fx. -- Subscribe a topic="a/b". -- Send a message, topic="a/b", payload="123" -- Subscriber will get a message with topic="a/b" and payload="hello". test.lua modifies the payload. - -If there are "test1.lua", "test2.lua" and "test3.lua" in /emqx/data/scripts, all these files will be loaded once emq-lua-hook get started. - -If test2.lua has been changed, restart emq-lua-hook to reload all scripts, or execute following command to reload test2.lua only: - -``` -/emqx/bin/emqx_ctl luahook reload test2.lua -``` - - -# Hook API - -You can find all example codes in the `examples.lua` file. - -## on_client_connected - -```lua -function on_client_connected(clientId, userName, returncode) - return 0 -end -``` -This API is called after a mqtt client has establish a connection with broker. - -### Input -* clientid : a string, mqtt client id. -* username : a string mqtt username -* returncode : a string, has following values - - success : Connection accepted - - Others is failed reason - -### Output -Needless - -## on_client_disconnected - -```lua -function on_client_disconnected(clientId, username, error) - return -end -``` -This API is called after a mqtt client has disconnected. - -### Input -* clientId : a string, mqtt client id. -* username : a string mqtt username -* error : a string, denote the disconnection reason. - -### Output -Needless - -## on_client_subscribe - -```lua -function on_client_subscribe(clientId, username, topic) - -- do your job here - if some_condition then - return new_topic - else - return false - end -end -``` -This API is called before mqtt engine process client's subscribe command. It is possible to change topic or cancel it. - -### Input -* clientid : a string, mqtt client id. -* username : a string mqtt username -* topic : a string, mqtt message's topic - -### Output -* new_topic : a string, change mqtt message's topic -* false : cancel subscription - - -## on_client_unsubscribe - -```lua - function on_client_unsubscribe(clientId, username, topic) - -- do your job here - if some_condition then - return new_topic - else - return false - end -end -``` -This API is called before mqtt engine process client's unsubscribe command. It is possible to change topic or cancel it. - -### Input -* clientid : a string, mqtt client id. -* username : a string mqtt username -* topic : a string, mqtt message's topic - -### Output -* new_topic : a string, change mqtt message's topic -* false : cancel unsubscription - - -## on_session_subscribed - -```lua -function on_session_subscribed(ClientId, Username, Topic) - return -end -``` -This API is called after a subscription has been done. - -### Input -* clientid : a string, mqtt client id. -* username : a string mqtt username -* topic : a string, mqtt's topic filter. - -### Output -Needless - - -## on_session_unsubscribed - -```lua -function on_session_unsubscribed(clientid, username, topic) - return -end -``` -This API is called after a unsubscription has been done. - -### Input -* clientid : a string, mqtt client id. -* username : a string mqtt username -* topic : a string, mqtt's topic filter. - -### Output -Needless - -## on_message_delivered - -```lua -function on_message_delivered(clientid, username, topic, payload, qos, retain) - -- do your job here - return topic, payload, qos, retain -end -``` -This API is called after a message has been pushed to mqtt clients. - -### Input -* clientId : a string, mqtt client id. -* username : a string mqtt username -* topic : a string, mqtt message's topic -* payload : a string, mqtt message's payload -* qos : a number, mqtt message's QOS (0, 1, 2) -* retain : a boolean, mqtt message's retain flag - -### Output -Needless - -## on_message_acked - -```lua -function on_message_acked(clientId, username, topic, payload, qos, retain) - return -end -``` -This API is called after a message has been acknowledged. - -### Input -* clientId : a string, mqtt client id. -* username : a string mqtt username -* topic : a string, mqtt message's topic -* payload : a string, mqtt message's payload -* qos : a number, mqtt message's QOS (0, 1, 2) -* retain : a boolean, mqtt message's retain flag - -### Output -Needless - -## on_message_publish - -```lua -function on_message_publish(clientid, username, topic, payload, qos, retain) - -- do your job here - if some_condition then - return new_topic, new_payload, new_qos, new_retain - else - return false - end -end -``` -This API is called before publishing message into mqtt engine. It's possible to change message or cancel publish in this API. - -### Input -* clientid : a string, mqtt client id of publisher. -* username : a string, mqtt username of publisher -* topic : a string, mqtt message's topic -* payload : a string, mqtt message's payload -* qos : a number, mqtt message's QOS (0, 1, 2) -* retain : a boolean, mqtt message's retain flag - -### Output -* new_topic : a string, change mqtt message's topic -* new_payload : a string, change mqtt message's payload -* new_qos : a number, change mqtt message's QOS -* new_retain : a boolean, change mqtt message's retain flag -* false : cancel publishing this mqtt message - -## register_hook - -```lua -function register_hook() - return "hook_name" -end - --- Or register multiple callbacks - -function register_hook() - return "hook_name1", "hook_name2", ... , "hook_nameX" -end -``` - -This API exports hook(s) implemented in its lua script. - -### Output -* hook_name must be a string, which is equal to the hook API(s) implemented. Possible values: - - "on_client_connected" - - "on_client_disconnected" - - "on_client_subscribe" - - "on_client_unsubscribe" - - "on_session_subscribed" - - "on_session_unsubscribed" - - "on_message_delivered" - - "on_message_acked" - - "on_message_publish" - -# management command - -## load - -```shell -emqx_ctl luahook load script_name -``` -This command will load lua file "script_name" in 'data/scripts' directory, into emqx hook. - -## unload - -```shell -emqx_ctl luahook unload script_name -``` -This command will unload lua file "script_name" out of emqx hook. - -## reload - -```shell -emqx_ctl luahook reload script_name -``` -This command will reload lua file "script_name" in 'data/scripts'. It is useful if a lua script has been modified and apply it immediately. - -## enable - -```shell -emqx_ctl luahook enable script_name -``` -This command will rename lua file "script_name.x" to "script_name", and load it immediately. - -## disable - -```shell -emqx_ctl luahook disable script_name -``` -This command will unload this script, and rename lua file "script_name" to "script_name.x", which will not be loaded during next boot. - - -License -------- - -Apache License Version 2.0 - -Author ------- - -EMQ X Team. - diff --git a/apps/emqx_lua_hook/etc/emqx_lua_hook.conf b/apps/emqx_lua_hook/etc/emqx_lua_hook.conf deleted file mode 100644 index f0256afae..000000000 --- a/apps/emqx_lua_hook/etc/emqx_lua_hook.conf +++ /dev/null @@ -1,4 +0,0 @@ -##-------------------------------------------------------------------- -## EMQ X Lua Hook -##-------------------------------------------------------------------- - diff --git a/apps/emqx_lua_hook/examples.lua b/apps/emqx_lua_hook/examples.lua deleted file mode 100644 index bc36eb771..000000000 --- a/apps/emqx_lua_hook/examples.lua +++ /dev/null @@ -1,71 +0,0 @@ --- --- Given all funcation names needed register to system --- -function register_hook() - return "on_client_connected", - "on_client_disconnected", - "on_client_subscribe", - "on_client_unsubscribe", - "on_session_subscribed", - "on_session_unsubscribed", - "on_message_delivered", - "on_message_acked", - "on_message_publish" -end - ----------------------------------------------------------------------- --- Callback Functions - -function on_client_connected(clientid, username, returncode) - print("Lua: on_client_connected - " .. clientid) - -- do your job here - return -end - -function on_client_disconnected(clientid, username, reason) - print("Lua: on_client_disconnected - " .. clientid) - -- do your job here - return -end - -function on_client_subscribe(clientid, username, topic) - print("Lua: on_client_subscribe - " .. clientid) - -- do your job here - return topic -end - -function on_client_unsubscribe(clientid, username, topic) - print("Lua: on_client_unsubscribe - " .. clientid) - -- do your job here - return topic -end - -function on_session_subscribed(clientid, username, topic) - print("Lua: on_session_subscribed - " .. clientid) - -- do your job here - return -end - -function on_session_unsubscribed(clientid, username, topic) - print("Lua: on_session_unsubscribed - " .. clientid) - -- do your job here - return -end - -function on_message_delivered(clientid, username, topic, payload, qos, retain) - print("Lua: on_message_delivered - " .. clientid) - -- do your job here - return topic, payload, qos, retain -end - -function on_message_acked(clientid, username, topic, payload, qos, retain) - print("Lua: on_message_acked- " .. clientid) - -- do your job here - return -end - -function on_message_publish(clientid, username, topic, payload, qos, retain) - print("Lua: on_message_publish - " .. clientid) - -- do your job here - return topic, payload, qos, retain -end diff --git a/apps/emqx_lua_hook/include/emqx_lua_hook.hrl b/apps/emqx_lua_hook/include/emqx_lua_hook.hrl deleted file mode 100644 index fb7010c2b..000000000 --- a/apps/emqx_lua_hook/include/emqx_lua_hook.hrl +++ /dev/null @@ -1,18 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --define(LOG(Level, Format, Args), emqx_logger:Level("Lua Hook: " ++ Format, Args)). - diff --git a/apps/emqx_lua_hook/priv/emqx_lua_hook.schema b/apps/emqx_lua_hook/priv/emqx_lua_hook.schema deleted file mode 100644 index 8b1378917..000000000 --- a/apps/emqx_lua_hook/priv/emqx_lua_hook.schema +++ /dev/null @@ -1 +0,0 @@ - diff --git a/apps/emqx_lua_hook/rebar.config b/apps/emqx_lua_hook/rebar.config deleted file mode 100644 index 97a06a77c..000000000 --- a/apps/emqx_lua_hook/rebar.config +++ /dev/null @@ -1,21 +0,0 @@ -{deps, - [{luerl, {git, "https://github.com/emqx/luerl", {tag, "v0.3.1"}}} - ]}. - -{edoc_opts, [{preprocess, true}]}. -{erl_opts, [warn_unused_vars, - warn_shadow_vars, - warn_unused_import, - warn_obsolete_guard, - debug_info, - compressed, - {parse_transform} - ]}. -{overrides, [{add, [{erl_opts, [compressed]}]}]}. - -{xref_checks, [undefined_function_calls, undefined_functions, - locals_not_used, deprecated_function_calls, - warnings_as_errors, deprecated_functions]}. -{cover_enabled, true}. -{cover_opts, [verbose]}. -{cover_export_enabled, true}. diff --git a/apps/emqx_lua_hook/src/emqx_lua_hook.app.src b/apps/emqx_lua_hook/src/emqx_lua_hook.app.src deleted file mode 100644 index 627c8e29d..000000000 --- a/apps/emqx_lua_hook/src/emqx_lua_hook.app.src +++ /dev/null @@ -1,14 +0,0 @@ -{application, emqx_lua_hook, - [{description, "EMQ X Lua Hooks"}, - {vsn, "4.3.0"}, % strict semver, bump manually! - {modules, []}, - {registered, []}, - {applications, [kernel,stdlib]}, - {mod, {emqx_lua_hook_app,[]}}, - {env,[]}, - {licenses, ["Apache-2.0"]}, - {maintainers, ["EMQ X Team "]}, - {links, [{"Homepage", "https://emqx.io/"}, - {"Github", "https://github.com/emqx/emqx-lua-hook"} - ]} - ]}. diff --git a/apps/emqx_lua_hook/src/emqx_lua_hook.erl b/apps/emqx_lua_hook/src/emqx_lua_hook.erl deleted file mode 100644 index 6e7810827..000000000 --- a/apps/emqx_lua_hook/src/emqx_lua_hook.erl +++ /dev/null @@ -1,199 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_lua_hook). - --behaviour(gen_server). - --include("emqx_lua_hook.hrl"). --include_lib("luerl/src/luerl.hrl"). - --export([ start_link/0 - , stop/0 - ]). - --export([ load_scripts/0 - , unload_scripts/0 - , load_script/1 - , unload_script/1 - ]). - --export([ init/1 - , handle_call/3 - , handle_cast/2 - , handle_info/2 - , terminate/2 - , code_change/3 - ]). - --export([lua_dir/0]). - --define(SERVER, ?MODULE). - --record(state, {loaded_scripts = []}). - -start_link() -> - gen_server:start_link({local, ?SERVER}, ?MODULE, {}, []). - -stop() -> - gen_server:call(?SERVER, stop). - -load_scripts() -> - gen_server:call(?SERVER, load_scripts). - -unload_scripts() -> - gen_server:call(?SERVER, unload_scrips). - -load_script(ScriptName) -> - gen_server:call(?SERVER, {load_script, ScriptName}). - -unload_script(ScriptName) -> - gen_server:call(?SERVER, {unload_script, ScriptName}). - -lua_dir() -> - filename:join([emqx:get_env(data_dir, "data"), "scripts"]). - -%%----------------------------------------------------------------------------- -%% gen_server callbacks -%%----------------------------------------------------------------------------- - -init({}) -> - {ok, #state{}}. - -handle_call(stop, _From, State) -> - {stop, normal, ok, State}; - -handle_call(load_scripts, _From, State) -> - {reply, ok, State#state{loaded_scripts = do_loadall()}, hibernate}; - -handle_call(unload_scrips, _From, State=#state{loaded_scripts = Scripts}) -> - do_unloadall(Scripts), - {reply, ok, State#state{loaded_scripts = []}, hibernate}; - -handle_call({load_script, ScriptName}, _From, State=#state{loaded_scripts = Scripts}) -> - {Ret, NewScripts} = case do_load(ScriptName) of - error -> {error, Scripts}; - {ScriptName, LuaState} -> - case lists:member({ScriptName, LuaState}, Scripts) of - true -> {ok, Scripts}; - false -> {ok, lists:append([{ScriptName, LuaState}], Scripts)} - end - end, - {reply, Ret, State#state{loaded_scripts = NewScripts}, hibernate}; - -handle_call({unload_script, ScriptName}, _From, State=#state{loaded_scripts = Scripts}) -> - case proplists:get_all_values(ScriptName, Scripts) of - [] -> - {reply, ok, State, hibernate}; - LuaStates -> - lists:foreach(fun(LuaState) -> - % Unload first! If this gen_server has been crashed, loaded_scripts will be empty - do_unload({ScriptName, LuaState}) - end, LuaStates), - NewScripts = proplists:delete(ScriptName, Scripts), - {reply, ok, State#state{loaded_scripts = NewScripts}, hibernate} - end; - -handle_call(Request, From, State) -> - ?LOG(error, "Unknown Request=~p from ~p", [Request, From]), - {reply, ignored, State, hibernate}. - -handle_cast(Msg, State) -> - ?LOG(error, "unexpected cast: ~p", [Msg]), - {noreply, State, hibernate}. - -handle_info(Info, State) -> - ?LOG(error, "unexpected info: ~p", [Info]), - {noreply, State}. - -terminate(_Reason, #state{loaded_scripts = Scripts}) -> - do_unloadall(Scripts), - ok. - -code_change(_OldVsn, State, _Extra) -> - {ok, State}. - -%% ------------------------------------------------------------------ -%% Internal Function Definitions -%% ------------------------------------------------------------------ - -do_loadall() -> - FileList = filelib:wildcard(filename:join([lua_dir(), "*.lua"])), - List = [do_load(X) || X <- FileList], - [X || X <- List, is_tuple(X)]. - -do_load(FileName) -> - case catch luerl:dofile(FileName) of - {'EXIT', St00} -> - ?LOG(error, "Failed to load lua script ~p due to error ~p", [FileName, St00]), - error; - {_Ret, St0=#luerl{}} -> - case catch luerl:call_function([register_hook], [], St0) of - {'EXIT', St1} -> - ?LOG(error, "Failed to execute register_hook function in lua script ~p, which has syntax error, St1=~p", [FileName, St1]), - error; - {Ret1, St1} -> - ?LOG(debug, "Register lua script ~p", [FileName]), - _ = do_register_hooks(Ret1, FileName, St1), - {FileName, St1}; - Other -> - ?LOG(error, "Failed to load lua script ~p, register_hook() raise exception ~p", [FileName, Other]), - error - end; - Exception -> - ?LOG(error, "Failed to load lua script ~p with error ~p", [FileName, Exception]), - error - end. - -do_register(<<"on_message_publish">>, ScriptName, St) -> - emqx_lua_script:register_on_message_publish(ScriptName, St); -do_register(<<"on_message_delivered">>, ScriptName, St) -> - emqx_lua_script:register_on_message_delivered(ScriptName, St); -do_register(<<"on_message_acked">>, ScriptName, St) -> - emqx_lua_script:register_on_message_acked(ScriptName, St); -do_register(<<"on_client_connected">>, ScriptName, St) -> - emqx_lua_script:register_on_client_connected(ScriptName, St); -do_register(<<"on_client_subscribe">>, ScriptName, St) -> - emqx_lua_script:register_on_client_subscribe(ScriptName, St); -do_register(<<"on_client_unsubscribe">>, ScriptName, St) -> - emqx_lua_script:register_on_client_unsubscribe(ScriptName, St); -do_register(<<"on_client_disconnected">>, ScriptName, St) -> - emqx_lua_script:register_on_client_disconnected(ScriptName, St); -do_register(<<"on_session_subscribed">>, ScriptName, St) -> - emqx_lua_script:register_on_session_subscribed(ScriptName, St); -do_register(<<"on_client_authenticate">>, ScriptName, St) -> - emqx_lua_script:register_on_client_authenticate(ScriptName, St); -do_register(<<"on_client_check_acl">>, ScriptName, St) -> - emqx_lua_script:register_on_client_check_acl(ScriptName, St); -do_register(Hook, ScriptName, _St) -> - ?LOG(error, "Discard unknown hook ~p ScriptName=~p", [Hook, ScriptName]). - -do_register_hooks([], _ScriptName, _St) -> - ok; -do_register_hooks([H|T], ScriptName, St) -> - _ = do_register(H, ScriptName, St), - do_register_hooks(T, ScriptName, St); -do_register_hooks(Hook = <<$o, $n, _Rest/binary>>, ScriptName, St) -> - do_register(Hook, ScriptName, St); -do_register_hooks(Hook, ScriptName, _St) -> - ?LOG(error, "Discard unknown hook type ~p from ~p", [Hook, ScriptName]). - -do_unloadall(Scripts) -> - lists:foreach(fun do_unload/1, Scripts). - -do_unload(Script) -> - emqx_lua_script:unregister_hooks(Script), - ok. diff --git a/apps/emqx_lua_hook/src/emqx_lua_hook_app.erl b/apps/emqx_lua_hook/src/emqx_lua_hook_app.erl deleted file mode 100644 index 6b0ec3574..000000000 --- a/apps/emqx_lua_hook/src/emqx_lua_hook_app.erl +++ /dev/null @@ -1,40 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_lua_hook_app). - --behaviour(application). - --emqx_plugin(?MODULE). - --export([ start/2 - , stop/1 - , prep_stop/1 - ]). - -start(_Type, _Args) -> - {ok, Sup} = emqx_lua_hook_sup:start_link(), - emqx_lua_hook:load_scripts(), - emqx_lua_hook_cli:load(), - {ok, Sup}. - -prep_stop(State) -> - emqx_lua_hook:unload_scripts(), - emqx_lua_hook_cli:unload(), - State. - -stop(_State) -> - ok. diff --git a/apps/emqx_lua_hook/src/emqx_lua_hook_cli.erl b/apps/emqx_lua_hook/src/emqx_lua_hook_cli.erl deleted file mode 100644 index 83c6fc5ef..000000000 --- a/apps/emqx_lua_hook/src/emqx_lua_hook_cli.erl +++ /dev/null @@ -1,88 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_lua_hook_cli). - --export([ load/0 - , cmd/1 - , unload/0 - ]). - --include("emqx_lua_hook.hrl"). --include_lib("luerl/src/luerl.hrl"). - --define(PRINT(Format, Args), io:format(Format, Args)). --define(PRINT_CMD(Cmd, Descr), io:format("~-48s# ~s~n", [Cmd, Descr])). --define(USAGE(CmdList), [?PRINT_CMD(Cmd, Descr) || {Cmd, Descr} <- CmdList]). - -load() -> - emqx_ctl:register_command(luahook, {?MODULE, cmd}, []). - -unload() -> - emqx_ctl:unregister_command(luahook). - -cmd(["load", Script]) -> - case emqx_lua_hook:load_script(fullname(Script)) of - ok -> emqx_ctl:print("Load ~p successfully~n", [Script]); - error -> emqx_ctl:print("Load ~p error~n", [Script]) - end; - -cmd(["reload", Script]) -> - FullName = fullname(Script), - emqx_lua_hook:unload_script(FullName), - case emqx_lua_hook:load_script(FullName) of - ok -> emqx_ctl:print("Reload ~p successfully~n", [Script]); - error -> emqx_ctl:print("Reload ~p error~n", [Script]) - end; - -cmd(["unload", Script]) -> - emqx_lua_hook:unload_script(fullname(Script)), - emqx_ctl:print("Unload ~p successfully~n", [Script]); - -cmd(["enable", Script]) -> - FullName = fullname(Script), - case file:rename(fullnamedisable(Script), FullName) of - ok -> case emqx_lua_hook:load_script(FullName) of - ok -> - emqx_ctl:print("Enable ~p successfully~n", [Script]); - error -> - emqx_ctl:print("Fail to enable ~p~n", [Script]) - end; - {error, Reason} -> - emqx_ctl:print("Fail to enable ~p due to ~p~n", [Script, Reason]) - end; - -cmd(["disable", Script]) -> - FullName = fullname(Script), - emqx_lua_hook:unload_script(FullName), - case file:rename(FullName, fullnamedisable(Script)) of - ok -> - emqx_ctl:print("Disable ~p successfully~n", [Script]); - {error, Reason} -> - emqx_ctl:print("Fail to disable ~p due to ~p~n", [Script, Reason]) - end; - -cmd(_) -> - emqx_ctl:usage([{"luahook load