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-----