Add more cleanups

This commit is contained in:
Zaiming Shi 2020-12-05 02:37:19 +01:00
parent 686c006d6e
commit 8a12018863
86 changed files with 1042 additions and 2767 deletions

View File

View File

@ -1,26 +0,0 @@
name: Run test cases
on: [push, pull_request]
jobs:
run_test_cases:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: run test cases
run: |
docker network create --driver bridge --ipv6 --subnet fd15:555::/64 tests_emqx_bridge
docker run -i \
--network tests_emqx_bridge \
-v $(pwd):/emqx_auth_http \
erlang:22.3 \
bash -c "make -C /emqx_auth_http xref
make -C /emqx_auth_http eunit
make -C /emqx_auth_http ct
make -C /emqx_auth_http cover"
- uses: actions/upload-artifact@v1
if: failure()
with:
name: logs
path: _build/test/logs

View File

@ -1,24 +0,0 @@
%%-*- mode: erlang -*-
%% .app.src.script
RemoveLeadingV =
fun(Tag) ->
case re:run(Tag, "^[v|e]?[0-9]\.[0-9]\.([0-9]|(rc|beta|alpha)\.[0-9])", [{capture, none}]) of
nomatch ->
re:replace(Tag, "/", "-", [{return ,list}]);
_ ->
%% if it is a version number prefixed by 'v' or 'e', then remove it
re:replace(Tag, "[v|e]", "", [{return ,list}])
end
end,
case os:getenv("EMQX_DEPS_DEFAULT_VSN") of
false -> CONFIG; % env var not defined
[] -> CONFIG; % env var set to empty string
Tag ->
[begin
AppConf0 = lists:keystore(vsn, 1, AppConf, {vsn, RemoveLeadingV(Tag)}),
{application, App, AppConf0}
end || Conf = {application, App, AppConf} <- CONFIG]
end.

View File

@ -1,29 +0,0 @@
name: Run test cases
on: [push, pull_request]
jobs:
run_test_cases:
runs-on: ubuntu-latest
container:
image: erlang:22.1
steps:
- uses: actions/checkout@v1
- name: run test cases
run: |
make xref
make eunit
make ct
make cover
- uses: actions/upload-artifact@v1
if: always()
with:
name: logs
path: _build/test/logs
- uses: actions/upload-artifact@v1
with:
name: cover
path: _build/test/cover

View File

@ -1,24 +0,0 @@
%%-*- mode: erlang -*-
%% .app.src.script
RemoveLeadingV =
fun(Tag) ->
case re:run(Tag, "^[v|e]?[0-9]\.[0-9]\.([0-9]|(rc|beta|alpha)\.[0-9])", [{capture, none}]) of
nomatch ->
re:replace(Tag, "/", "-", [{return ,list}]);
_ ->
%% if it is a version number prefixed by 'v' or 'e', then remove it
re:replace(Tag, "[v|e]", "", [{return ,list}])
end
end,
case os:getenv("EMQX_DEPS_DEFAULT_VSN") of
false -> CONFIG; % env var not defined
[] -> CONFIG; % env var set to empty string
Tag ->
[begin
AppConf0 = lists:keystore(vsn, 1, AppConf, {vsn, RemoveLeadingV(Tag)}),
{application, App, AppConf0}
end || Conf = {application, App, AppConf} <- CONFIG]
end.

View File

@ -1,26 +0,0 @@
version: '3'
services:
erlang:
image: erlang:22.1
volumes:
- ../:/emqx_auth_ldap
networks:
- emqx_bridge
depends_on:
- ldap_server
tty: true
ldap_server:
build: ./emqx-ldap
image: emqx-ldap:1.0
restart: always
ports:
- 389:389
- 636:636
networks:
- emqx_bridge
networks:
emqx_bridge:
driver: bridge

View File

@ -1,26 +0,0 @@
FROM buildpack-deps:stretch
ENV VERSION=2.4.50
RUN apt-get update && apt-get install -y groff groff-base
RUN wget ftp://ftp.openldap.org/pub/OpenLDAP/openldap-release/openldap-${VERSION}.tgz \
&& gunzip -c openldap-${VERSION}.tgz | tar xvfB - \
&& cd openldap-${VERSION} \
&& ./configure && make depend && make && make install \
&& cd .. && rm -rf openldap-${VERSION}
COPY ./slapd.conf /usr/local/etc/openldap/slapd.conf
COPY ./emqx.io.ldif /usr/local/etc/openldap/schema/emqx.io.ldif
COPY ./emqx.schema /usr/local/etc/openldap/schema/emqx.schema
COPY ./*.pem /usr/local/etc/openldap/
RUN mkdir -p /usr/local/etc/openldap/data \
&& slapadd -l /usr/local/etc/openldap/schema/emqx.io.ldif -f /usr/local/etc/openldap/slapd.conf
WORKDIR /usr/local/etc/openldap
EXPOSE 389 636
ENTRYPOINT ["/usr/local/libexec/slapd", "-h", "ldap:/// ldaps:///", "-d", "3", "-f", "/usr/local/etc/openldap/slapd.conf"]
CMD []

View File

@ -1,16 +0,0 @@
include /usr/local/etc/openldap/schema/core.schema
include /usr/local/etc/openldap/schema/cosine.schema
include /usr/local/etc/openldap/schema/inetorgperson.schema
include /usr/local/etc/openldap/schema/ppolicy.schema
include /usr/local/etc/openldap/schema/emqx.schema
TLSCACertificateFile /usr/local/etc/openldap/cacert.pem
TLSCertificateFile /usr/local/etc/openldap/cert.pem
TLSCertificateKeyFile /usr/local/etc/openldap/key.pem
database bdb
suffix "dc=emqx,dc=io"
rootdn "cn=root,dc=emqx,dc=io"
rootpw {SSHA}eoF7NhNrejVYYyGHqnt+MdKNBh4r1w3W
directory /usr/local/etc/openldap/data

View File

@ -1,49 +0,0 @@
name: Run test cases
on: [push, pull_request]
jobs:
run_test_cases:
runs-on: ubuntu-latest
strategy:
matrix:
network_type:
- ipv4
- ipv6
steps:
- name: install docker-compose
run: |
sudo curl -L "https://github.com/docker/compose/releases/download/1.25.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
- uses: actions/checkout@v1
- name: prepare
env:
NETWORK_TYPE: ${{ matrix.network_type }}
run: |
set -e -x -u
cp ./emqx.io.ldif ./emqx.schema ./.ci/emqx-ldap
cp ./test/certs/* ./.ci/emqx-ldap
docker-compose -f ./.ci/docker-compose.yml -p tests build
if [ "$NETWORK_TYPE" = "ipv6" ];then docker network create --driver bridge --ipv6 --subnet fd15:555::/64 tests_emqx_bridge --attachable; fi
docker-compose -f ./.ci/docker-compose.yml -p tests up -d
if [ "$NETWORK_TYPE" != "ipv6" ];then
docker exec -i tests_erlang_1 sh -c "sed -i '/auth.ldap.servers/c auth.ldap.servers = ldap_server' ./emqx_auth_ldap/etc/emqx_auth_ldap.conf"
else
ipv6_address=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.GlobalIPv6Address}}{{end}}' $(docker ps -a -f name=tests_ldap_server_1 -q))
docker exec -i $(docker ps -a -f name=tests_erlang_1 -q) sh -c "sed -i '/auth.ldap.servers/c auth.ldap.servers = $ipv6_address' /emqx_auth_ldap/etc/emqx_auth_ldap.conf"
fi
- name: run test cases
run: |
set -e -x -u
docker exec -i tests_erlang_1 sh -c "make -C /emqx_auth_ldap xref"
docker exec -i tests_erlang_1 sh -c "make -C /emqx_auth_ldap eunit"
docker exec -i tests_erlang_1 sh -c "make -C /emqx_auth_ldap ct"
docker exec -i tests_erlang_1 sh -c "make -C /emqx_auth_ldap cover"
- uses: actions/upload-artifact@v1
if: failure()
with:
name: logs_${{ matrix.network_type }}
path: _build/test/logs

View File

@ -1,24 +0,0 @@
%%-*- mode: erlang -*-
%% .app.src.script
RemoveLeadingV =
fun(Tag) ->
case re:run(Tag, "^[v|e]?[0-9]\.[0-9]\.([0-9]|(rc|beta|alpha)\.[0-9])", [{capture, none}]) of
nomatch ->
re:replace(Tag, "/", "-", [{return ,list}]);
_ ->
%% if it is a version number prefixed by 'v' or 'e', then remove it
re:replace(Tag, "[v|e]", "", [{return ,list}])
end
end,
case os:getenv("EMQX_DEPS_DEFAULT_VSN") of
false -> CONFIG; % env var not defined
[] -> CONFIG; % env var set to empty string
Tag ->
[begin
AppConf0 = lists:keystore(vsn, 1, AppConf, {vsn, RemoveLeadingV(Tag)}),
{application, App, AppConf0}
end || Conf = {application, App, AppConf} <- CONFIG]
end.

View File

@ -1,29 +0,0 @@
name: Run test cases
on: [push, pull_request]
jobs:
run_test_cases:
runs-on: ubuntu-latest
container:
image: erlang:22.1
steps:
- uses: actions/checkout@v1
- name: run test cases
run: |
make xref
make eunit
make ct
make cover
- uses: actions/upload-artifact@v1
if: always()
with:
name: logs
path: _build/test/logs
- uses: actions/upload-artifact@v1
with:
name: cover
path: _build/test/cover

View File

@ -1,20 +1,30 @@
## Examples:
##auth.mnesia.1.login = admin
##auth.mnesia.1.password = public
##auth.mnesia.1.is_superuser = true
##auth.mnesia.2.login = feng@emqtt.io
##auth.mnesia.2.password = public
##auth.mnesia.2.is_superuser = false
##auth.mnesia.3.login = name~!@#$%^&*()_+
##auth.mnesia.3.password = pwsswd~!@#$%^&*()_+
##auth.mnesia.3.is_superuser = false
## Password hash.
##
## Value: plain | md5 | sha | sha256
## Value: plain | md5 | sha | sha256 | sha512
auth.mnesia.password_hash = sha256
## Auth as username or auth as clientid.
##
## Value: username | clientid
auth.mnesia.as = username
##--------------------------------------------------------------------
## 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~!@#$%^&*()_+

View File

@ -1,16 +1,19 @@
-define(APP, emqx_auth_mnesia).
-type(login():: {clientid, binary()}
| {username, binary()}).
-record(emqx_user, {
login,
password,
is_superuser
login :: login(),
password :: binary(),
created_at :: integer()
}).
-record(emqx_acl, {
login,
topic,
action,
allow
filter:: {login() | all, emqx_topic:topic()},
action :: pub | sub | pubsub,
access :: allow | deny,
created_at :: integer()
}).
-record(auth_metrics, {

View File

@ -1,34 +1,42 @@
%%-*- mode: erlang -*-
%% emqx_auth_mnesia config mapping
{mapping, "auth.mnesia.as", "emqx_auth_mnesia.as", [
{default, username},
{datatype, {enum, [username, clientid]}}
]}.
{mapping, "auth.mnesia.password_hash", "emqx_auth_mnesia.password_hash", [
{default, sha256},
{datatype, {enum, [plain, md5, sha, sha256]}}
{datatype, {enum, [plain, md5, sha, sha256, sha512]}}
]}.
{mapping, "auth.mnesia.$id.login", "emqx_auth_mnesia.userlist", [
{mapping, "auth.client.$id.clientid", "emqx_auth_mnesia.clientid_list", [
{datatype, string}
]}.
{mapping, "auth.mnesia.$id.password", "emqx_auth_mnesia.userlist", [
{mapping, "auth.client.$id.password", "emqx_auth_mnesia.clientid_list", [
{datatype, string}
]}.
{mapping, "auth.mnesia.$id.is_superuser", "emqx_auth_mnesia.userlist", [
{default, false},
{datatype, {enum, [false, true]}}
]}.
{translation, "emqx_auth_mnesia.userlist", fun(Conf) ->
Userlist = cuttlefish_variable:filter_by_prefix("auth.mnesia", Conf),
{translation, "emqx_auth_mnesia.clientid_list", fun(Conf) ->
ClientList = cuttlefish_variable:filter_by_prefix("auth.client", Conf),
lists:foldl(
fun({["auth", "mnesia", Id, "login"], Username}, AccIn) ->
[{Username, cuttlefish:conf_get("auth.mnesia." ++ Id ++ ".password", Conf), cuttlefish:conf_get("auth.mnesia." ++ Id ++ ".is_superuser", Conf)} | AccIn];
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)

View File

@ -2,7 +2,7 @@
{deps,
[{emqx_passwd, {git, "https://github.com/emqx/emqx-passwd.git", {tag, "v1.1.1"}}},
{minirest, {git, "https://github.com/emqx/minirest.git", {tag, "0.3.1"}}}
{minirest, {git, "https://github.com/emqx/minirest.git", {tag, "0.3.2"}}}
]}.
{profiles,

View File

@ -18,6 +18,10 @@
-include("emqx_auth_mnesia.hrl").
-include_lib("stdlib/include/ms_transform.hrl").
-define(TABLE, emqx_acl).
%% ACL Callbacks
-export([ init/0
, register_metrics/0
@ -27,22 +31,38 @@
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_user, disc_copies).
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, PubSub, Topic, NoMatchAction, #{key_as := As}) ->
Login = maps:get(As, ClientInfo),
case do_check_acl(Login, PubSub, Topic, NoMatchAction) 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}
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".
@ -51,33 +71,33 @@ description() -> "Acl with Mnesia".
%% Internal functions
%%-------------------------------------------------------------------
do_check_acl(Login, PubSub, Topic, _NoMatchAction) ->
case match(PubSub, Topic, emqx_auth_mnesia_cli:lookup_acl(Login)) of
allow -> {stop, allow};
deny -> {stop, deny};
_ ->
case match(PubSub, Topic, emqx_auth_mnesia_cli:lookup_acl(<<"$all">>)) of
allow -> {stop, allow};
deny -> {stop, deny};
_ -> ok
end
end.
match(_PubSub, _Topic, []) ->
match(_ClientInfo, _PubSub, _Topic, []) ->
nomatch;
match(PubSub, Topic, [ #emqx_acl{topic = ACLTopic, action = Action, allow = Allow} | UserAcl]) ->
case match_actions(PubSub, Action) andalso match_topic(Topic, ACLTopic) of
true -> case Allow of
true -> allow;
_ -> deny
end;
false -> match(PubSub, Topic, UserAcl)
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(Topic, ACLTopic) when is_binary(Topic) ->
emqx_topic:match(Topic, ACLTopic).
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(_, 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]).

View File

@ -1,4 +1,4 @@
%%--------------------------------------------------------------------
%c%--------------------------------------------------------------------
%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
@ -18,94 +18,174 @@
-include("emqx_auth_mnesia.hrl").
-import(proplists, [get_value/2]).
-include_lib("stdlib/include/ms_transform.hrl").
-import(proplists, [ get_value/2
, get_value/3
]).
-import(minirest, [return/1]).
-rest_api(#{name => list_emqx_acl,
-rest_api(#{name => list_clientid,
method => 'GET',
path => "/mqtt_acl",
func => list,
path => "/acl/clientid",
func => list_clientid,
descr => "List available mnesia in the cluster"
}).
-rest_api(#{name => lookup_emqx_acl,
-rest_api(#{name => list_username,
method => 'GET',
path => "/mqtt_acl/:bin:login",
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 => add_emqx_acl,
-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 => "/mqtt_acl",
path => "/acl",
func => add,
descr => "Add mnesia in the cluster"
}).
-rest_api(#{name => delete_emqx_acl,
-rest_api(#{name => delete_clientid,
method => 'DELETE',
path => "/mqtt_acl/:bin:login/:bin:topic",
path => "/acl/clientid/:bin:clientid/topic/:bin:topic",
func => delete,
descr => "Delete mnesia in the cluster"
}).
-export([ list/2
-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(_Bindings, Params) ->
return({ok, emqx_auth_mnesia_api:paginate(emqx_acl, Params, fun format/1)}).
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)}).
lookup(#{login := Login}, _Params) ->
return({ok, format(emqx_auth_mnesia_cli:lookup_acl(urldecode(Login)))}).
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(add_acl(Params, []));
false -> return(add_acl([Params], []))
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, Re})
end
end.
add_acl([ Params | ParamsN ], ReList ) ->
Login = urldecode(get_value(<<"login">>, Params)),
Topic = urldecode(get_value(<<"topic">>, Params)),
Action = urldecode(get_value(<<"action">>, Params)),
Allow = get_value(<<"allow">>, Params),
Re = case validate([login, topic, action, allow], [Login, Topic, Action, Allow]) of
ok ->
emqx_auth_mnesia_cli:add_acl(Login, Topic, Action, Allow);
Err -> Err
end,
add_acl(ParamsN, [{Login, format_msg(Re)} | ReList]);
do_add([ Params | ParamsN ], ReList) ->
do_add(ParamsN, [do_add(Params) | ReList]);
add_acl([], ReList) ->
do_add([], ReList) ->
{ok, ReList}.
delete(#{login := Login, topic := Topic}, _) ->
return(emqx_auth_mnesia_cli:remove_acl(urldecode(Login), urldecode(Topic))).
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(#emqx_acl{login = Login, topic = Topic, action = Action, allow = Allow}) ->
#{login => Login, topic => Topic, action => Action, allow => Allow };
format([]) ->
#{};
format([#emqx_acl{login = Login, topic = Topic, action = Action, allow = Allow}]) ->
format(#emqx_acl{login = Login, topic = Topic, action = Action, allow = Allow});
format([ #emqx_acl{login = _Key, topic = _Topic, action = _Action, allow = _Allow}| _] = List) ->
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([#emqx_acl{login = Login, topic = Topic, action = Action, allow = Allow} | List], ReList) ->
format(List, [ format(#emqx_acl{login = Login, topic = Topic, action = Action, allow = Allow}) | ReList]);
format([], ReList) -> ReList.
format([L | List], Relist) ->
format(List, [format(L) | Relist]);
format([], ReList) -> lists:reverse(ReList).
validate([], []) ->
ok;
@ -114,8 +194,18 @@ validate([K|Keys], [V|Values]) ->
false -> {error, K};
true -> validate(Keys, Values)
end.
do_validation(login, V) when is_binary(V)
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(clientid, V) when is_binary(V)
andalso byte_size(V) > 0 ->
true;
do_validation(username, V) when is_binary(V)
andalso byte_size(V) > 0 ->
true;
do_validation(topic, V) when is_binary(V)
@ -126,7 +216,7 @@ do_validation(action, V) when is_binary(V) ->
true -> true;
false -> false
end;
do_validation(allow, V) when is_boolean(V) ->
do_validation(access, V) when V =:= <<"allow">> orelse V =:= <<"deny">> ->
true;
do_validation(_, _) ->
false.
@ -145,4 +235,3 @@ urldecode(S) ->
urldecode(S) ->
http_uri:decode(S).
-endif.

View File

@ -0,0 +1,198 @@
%%--------------------------------------------------------------------
%% Copyright (c) 2020 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) ->
Acls = #?TABLE{
filter = {Login, Topic},
action = Action,
access = Access,
created_at = erlang:system_time(millisecond)
},
ret(mnesia:transaction(fun mnesia:write/1, [Acls])).
%% @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)).
%%--------------------------------------------------------------------
%% 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.
%%--------------------------------------------------------------------
%% ACL Cli
%%--------------------------------------------------------------------
cli(["list"]) ->
[ begin
case Filter of
{clientid, Clientid} ->
emqx_ctl:print("Acl(clientid = ~p topic = ~p action = ~p access = ~p)~n",[Clientid, Topic, Action, Access]);
{username, Username} ->
emqx_ctl:print("Acl(username = ~p topic = ~p action = ~p access = ~p)~n",[Username, Topic, Action, Access]);
all ->
emqx_ctl:print("Acl($all topic = ~p action = ~p access = ~p)~n",[Topic, Action, Access])
end
end || {Filter, Topic, Action, Access, _} <- all_acls()];
cli(["list", "clientid"]) ->
[emqx_ctl:print("Acl(clientid = ~p topic = ~p action = ~p access = ~p)~n",[Clientid, Topic, Action, Access])
|| {{clientid, Clientid}, Topic, Action, Access, _} <- all_acls(clientid) ];
cli(["list", "username"]) ->
[emqx_ctl:print("Acl(username = ~p topic = ~p action = ~p access = ~p)~n",[Username, Topic, Action, Access])
|| {{username, Username}, Topic, Action, Access, _} <- all_acls(username) ];
cli(["list", "_all"]) ->
[emqx_ctl:print("Acl($all topic = ~p action = ~p access = ~p)~n",[Topic, Action, Access])
|| {all, Topic, Action, Access, _} <- 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]) ->
[emqx_ctl:print("Acl(clientid = ~p topic = ~p action = ~p access = ~p)~n",[NClientid, Topic, Action, Access])
|| {{clientid, NClientid}, Topic, Action, Access, _} <- lookup_acl({clientid, iolist_to_binary(Clientid)}) ];
cli(["show", "username", Username]) ->
[emqx_ctl:print("Acl(username = ~p topic = ~p action = ~p access = ~p)~n",[NUsername, Topic, Action, Access])
|| {{username, NUsername}, Topic, Action, Access, _} <- lookup_acl({username, iolist_to_binary(Username)}) ];
cli(["del", "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])->
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])->
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 <Clientid>", "Lookup clientid acl detail"}
, {"acl show username <Username>", "Lookup username acl detail"}
, {"acl aad clientid <Clientid> <Topic> <Action> <Access>", "Add clientid acl"}
, {"acl add Username <Username> <Topic> <Action> <Access>", "Add username acl"}
, {"acl add _all <Topic> <Action> <Access>", "Add $all acl"}
, {"acl del clientid <Clientid> <Topic>", "Delete clientid acl"}
, {"acl del username <Username> <Topic>", "Delete username acl"}
, {"acl del _all, <Topic>", "Delete $all acl"}
]).

View File

@ -1,24 +0,0 @@
%%-*- mode: erlang -*-
%% .app.src.script
RemoveLeadingV =
fun(Tag) ->
case re:run(Tag, "^[v|e]?[0-9]\.[0-9]\.([0-9]|(rc|beta|alpha)\.[0-9])", [{capture, none}]) of
nomatch ->
re:replace(Tag, "/", "-", [{return ,list}]);
_ ->
%% if it is a version number prefixed by 'v' or 'e', then remove it
re:replace(Tag, "[v|e]", "", [{return ,list}])
end
end,
case os:getenv("EMQX_DEPS_DEFAULT_VSN") of
false -> CONFIG; % env var not defined
[] -> CONFIG; % env var set to empty string
Tag ->
[begin
AppConf0 = lists:keystore(vsn, 1, AppConf, {vsn, RemoveLeadingV(Tag)}),
{application, App, AppConf0}
end || Conf = {application, App, AppConf} <- CONFIG]
end.

View File

@ -22,6 +22,9 @@
-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
@ -29,48 +32,53 @@
, description/0
]).
init(DefaultUsers) ->
init(#{clientid_list := ClientidList, username_list := UsernameList}) ->
ok = ekka_mnesia:create_table(emqx_user, [
{disc_copies, [node()]},
{attributes, record_info(fields, emqx_user)},
{storage_properties, [{ets, [{read_concurrency, true}]}]}]),
ok = lists:foreach(fun add_default_user/1, DefaultUsers),
[ 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(emqx_user, disc_copies).
%% @private
add_default_user({Login, Password, IsSuperuser}) ->
emqx_auth_mnesia_cli:add_user(iolist_to_binary(Login), iolist_to_binary(Password), IsSuperuser).
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 = #{password := Password}, AuthResult, #{hash_type := HashType, key_as := As}) ->
Login = maps:get(As, ClientInfo),
case emqx_auth_mnesia_cli:lookup_user(Login) of
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;
[User] ->
case emqx_passwd:check_pass({User#emqx_user.password, Password}, HashType) of
ok ->
emqx_metrics:inc(?AUTH_METRICS(success)),
{stop, AuthResult#{is_superuser => is_superuser(User),
anonymous => false,
auth_result => success}};
{error, Reason} ->
?LOG(error, "[Mnesia] Auth from mnesia failed: ~p", [Reason]),
List ->
case [ Hash || <<Salt:4/binary, Hash/binary>> <- lists:sort(fun emqx_auth_mnesia_cli:comparing/2, List),
Hash =:= hash(NPassword, Salt, HashType)
] of
[] ->
?LOG(error, "[Mnesia] Auth from mnesia failed: ~p", [ClientInfo]),
emqx_metrics:inc(?AUTH_METRICS(failure)),
{stop, AuthResult#{auth_result => password_error, anonymous => false}}
{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".
%%--------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------
is_superuser(#emqx_user{is_superuser = true}) ->
true;
is_superuser(_) ->
false.
hash(undefined, SaltBin, HashType) ->
hash(<<>>, SaltBin, HashType);
hash(Password, SaltBin, HashType) ->
emqx_passwd:hash(HashType, <<SaltBin/binary, Password/binary>>).

View File

@ -17,100 +17,206 @@
-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]).
-rest_api(#{name => list_emqx_user,
method => 'GET',
path => "/mqtt_user",
func => list,
descr => "List available mnesia in the cluster"
}).
-rest_api(#{name => lookup_emqx_user,
method => 'GET',
path => "/mqtt_user/:bin:login",
func => lookup,
descr => "Lookup mnesia in the cluster"
}).
-rest_api(#{name => add_emqx_user,
method => 'POST',
path => "/mqtt_user",
func => add,
descr => "Add mnesia in the cluster"
}).
-rest_api(#{name => update_emqx_user,
method => 'PUT',
path => "/mqtt_user/:bin:login",
func => update,
descr => "Update mnesia in the cluster"
}).
-rest_api(#{name => delete_emqx_user,
method => 'DELETE',
path => "/mqtt_user/:bin:login",
func => delete,
descr => "Delete mnesia in the cluster"
}).
-export([ list/2
, lookup/2
, add/2
, update/2
, delete/2
-export([ list_clientid/2
, lookup_clientid/2
, add_clientid/2
, update_clientid/2
, delete_clientid/2
]).
-export([paginate/3]).
-rest_api(#{name => list_clientid,
method => 'GET',
path => "/auth_clientid",
func => list_clientid,
descr => "List available clientid in the cluster"
}).
list(_Bindings, Params) ->
return({ok, paginate(emqx_user, Params, fun format/1)}).
-rest_api(#{name => lookup_clientid,
method => 'GET',
path => "/auth_clientid/:bin:clientid",
func => lookup_clientid,
descr => "Lookup clientid in the cluster"
}).
lookup(#{login := Login}, _Params) ->
return({ok, format(emqx_auth_mnesia_cli:lookup_user(urldecode(Login)))}).
-rest_api(#{name => add_clientid,
method => 'POST',
path => "/auth_clientid",
func => add_clientid,
descr => "Add clientid in the cluster"
}).
add(_Bindings, Params) ->
-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(add_user(Params, []));
false -> return(add_user([Params], []))
true -> return(do_add_clientid(Params, []));
false ->
Re = do_add_clientid(Params),
case Re of
ok -> return(ok);
<<"ok">> -> return(ok);
_ -> return({error, format_msg(Re)})
end
end.
add_user([ Params | ParamsN ], ReList ) ->
Login = urldecode(get_value(<<"login">>, Params)),
Password = urldecode(get_value(<<"password">>, Params)),
IsSuperuser = get_value(<<"is_superuser">>, Params),
Re = case validate([login, password, is_superuser], [Login, Password, IsSuperuser]) of
ok ->
emqx_auth_mnesia_cli:add_user(Login, Password, IsSuperuser);
Err -> Err
end,
add_user(ParamsN, [{Login, format_msg(Re)} | ReList]);
do_add_clientid([ Params | ParamsN ], ReList ) ->
Clientid = urldecode(get_value(<<"clientid">>, Params)),
do_add_clientid(ParamsN, [{Clientid, format_msg(do_add_clientid(Params))} | ReList]);
add_user([], ReList) ->
do_add_clientid([], ReList) ->
{ok, ReList}.
update(#{login := Login}, Params) ->
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),
IsSuperuser = get_value(<<"is_superuser">>, Params),
case validate([password, is_superuser], [Password, IsSuperuser]) of
ok -> return(emqx_auth_mnesia_cli:update_user(urldecode(Login), urldecode(Password), IsSuperuser));
case validate([password], [Password]) of
ok -> return(emqx_auth_mnesia_cli:update_user({clientid, urldecode(Clientid)}, urldecode(Password)));
Err -> return(Err)
end.
delete(#{login := Login}, _) ->
return(emqx_auth_mnesia_cli:remove_user(urldecode(Login))).
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);
<<"ok">> -> return(ok);
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, Params, RowFun) ->
Qh = query_handle(Tables),
Count = count(Tables),
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),
@ -121,21 +227,28 @@ paginate(Tables, Params, RowFun) ->
Rows = qlc:next_answers(Cursor, Limit),
qlc:delete_cursor(Cursor),
#{meta => #{page => Page, limit => Limit, count => Count},
data => [RowFun(Row) || Row <- Rows]}.
data => [RowFun(Row) || Row <- lists:sort(ComparingFun, Rows)]}.
query_handle(Table) when is_atom(Table) ->
qlc:q([R|| R <- ets:table(Table)]);
query_handle([Table]) when is_atom(Table) ->
qlc:q([R|| R <- ets:table(Table)]);
query_handle(Tables) ->
qlc:append([qlc:q([E || E <- ets:table(T)]) || T <- Tables]).
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) when is_atom(Table) ->
ets:info(Table, size);
count([Table]) when is_atom(Table) ->
ets:info(Table, size);
count(Tables) ->
lists:sum([count(T) || 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">>)).
@ -146,24 +259,28 @@ limit(Params) ->
Size -> binary_to_integer(Size)
end.
%%------------------------------------------------------------------------------
%% Interval Funcs
%%------------------------------------------------------------------------------
format({emqx_user, Login, Password, IsSuperuser}) ->
#{login => Login,
password => Password,
is_superuser => IsSuperuser};
format({?TABLE, {clientid, ClientId}, Password, _InterTime}) ->
#{clientid => ClientId,
password => Password};
format({?TABLE, {username, Username}, Password, _InterTime}) ->
#{username => Username,
password => Password};
format([{?TABLE, {clientid, ClientId}, Password, _InterTime}]) ->
#{clientid => ClientId,
password => Password};
format([{?TABLE, {username, Username}, Password, _InterTime}]) ->
#{username => Username,
password => Password};
format([]) ->
#{};
format([{emqx_user, Login, Password, IsSuperuser}]) ->
#{login => Login,
password => Password,
is_superuser => IsSuperuser}.
#{}.
validate([], []) ->
ok;
@ -173,14 +290,15 @@ validate([K|Keys], [V|Values]) ->
true -> validate(Keys, Values)
end.
do_validation(login, V) when is_binary(V)
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(is_superuser, V) when is_boolean(V) ->
true;
do_validation(_, _) ->
false.

View File

@ -34,8 +34,10 @@
start(_StartType, _StartArgs) ->
{ok, Sup} = emqx_auth_mnesia_sup:start_link(),
emqx_ctl:register_command('mqtt-user', {emqx_auth_mnesia_cli, auth_cli}, []),
emqx_ctl:register_command('mqtt-acl', {emqx_auth_mnesia_cli, acl_cli}, []),
emqx_ctl:register_command(clientid, {emqx_auth_mnesia_cli, auth_clientid_cli}, []),
emqx_ctl:register_command(username, {emqx_auth_mnesia_cli, auth_username_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}.
@ -43,28 +45,26 @@ start(_StartType, _StartArgs) ->
prep_stop(State) ->
emqx:unhook('client.authenticate', fun emqx_auth_mnesia:check/3),
emqx:unhook('client.check_acl', fun emqx_acl_mnesia:check_acl/5),
emqx_ctl:unregister_command('mqtt-user'),
emqx_ctl:unregister_command('mqtt-acl'),
emqx_ctl:unregister_command(clientid),
emqx_ctl:unregister_command(username),
emqx_ctl:unregister_command(user),
emqx_ctl:unregister_command(acl),
State.
stop(_State) ->
ok.
load_auth_hook() ->
DefaultUsers = application:get_env(?APP, userlist, []),
ok = emqx_auth_mnesia:init(DefaultUsers),
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, hash_type, sha256),
key_as => application:get_env(emqx_auth_mnesia, as, username)
hash_type => application:get_env(emqx_auth_mnesia, hash_type, sha256)
},
emqx:hook('client.authenticate', fun emqx_auth_mnesia:check/3, [Params]).
load_acl_hook() ->
ok = emqx_acl_mnesia:init(),
ok = emqx_acl_mnesia:register_metrics(),
Params = #{
key_as => application:get_env(emqx_auth_mnesia, as, username)
},
emqx:hook('client.check_acl', fun emqx_acl_mnesia:check_acl/5, [Params]).
emqx:hook('client.check_acl', fun emqx_acl_mnesia:check_acl/5, [#{}]).

View File

@ -18,31 +18,32 @@
-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/3
, update_user/3
-export([ add_user/2
, update_user/2
, remove_user/1
, lookup_user/1
, all_users/0
]).
%% Acl APIs
-export([ add_acl/4
, remove_acl/2
, lookup_acl/1
, all_acls/0
, all_users/1
]).
%% Cli
-export([ auth_cli/1
, acl_cli/1]).
-export([ auth_clientid_cli/1
, auth_username_cli/1
]).
%% Helper
-export([comparing/2]).
%%--------------------------------------------------------------------
%% Auth APIs
%%--------------------------------------------------------------------
%% @doc Add User
-spec(add_user(binary(), binary(), atom()) -> ok | {error, any()}).
add_user(Login, Password, IsSuperuser) ->
User = #emqx_user{login = Login, password = encrypted_data(Password), is_superuser = IsSuperuser},
-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}) ->
@ -52,30 +53,31 @@ insert_user(User = #emqx_user{login = Login}) ->
end.
%% @doc Update User
-spec(update_user(binary(), binary(), atom()) -> ok | {error, any()}).
update_user(Login, NewPassword, IsSuperuser) ->
User = #emqx_user{login = Login, password = encrypted_data(NewPassword), is_superuser = IsSuperuser},
-spec(update_user(tuple(), binary()) -> ok | {error, any()}).
update_user(Login, NewPassword) ->
User = #emqx_user{login = Login, password = encrypted_data(NewPassword)},
ret(mnesia:transaction(fun do_update_user/1, [User])).
do_update_user(User = #emqx_user{login = Login}) ->
case mnesia:read(?TABLE, Login) of
[_|_] -> mnesia:write(User);
[{?TABLE, Login, _, CreateAt}] -> mnesia:write(User#emqx_user{created_at = CreateAt});
[] -> mnesia:abort(noexisted)
end.
%% @doc Lookup user by login
-spec(lookup_user(binary()) -> list()).
-spec(lookup_user(tuple()) -> list()).
lookup_user(undefined) -> [];
lookup_user(Login) ->
case mnesia:dirty_read(?TABLE, Login) of
{error, Reason} ->
?LOG(error, "[Mnesia] do_check_user error: ~p~n", [Reason]),
[];
Re -> Re
Re ->
lists:sort(fun comparing/2, Re)
end.
%% @doc Remove user
-spec(remove_user(binary()) -> ok | {error, any()}).
-spec(remove_user(tuple()) -> ok | {error, any()}).
remove_user(Login) ->
ret(mnesia:transaction(fun mnesia:delete/1, [{?TABLE, Login}])).
@ -83,111 +85,97 @@ remove_user(Login) ->
-spec(all_users() -> list()).
all_users() -> mnesia:dirty_all_keys(?TABLE).
%%--------------------------------------------------------------------
%% Acl API
%%--------------------------------------------------------------------
%% @doc Add Acls
-spec(add_acl(binary(), binary(), binary(), atom()) -> ok | {error, any()}).
add_acl(Login, Topic, Action, Allow) ->
Acls = #emqx_acl{login = Login, topic = Topic, action = Action, allow = Allow},
ret(mnesia:transaction(fun mnesia:write/1, [Acls])).
%% @doc Lookup acl by login
-spec(lookup_acl(binary()) -> list()).
lookup_acl(undefined) -> [];
lookup_acl(Login) ->
case mnesia:dirty_read(emqx_acl, Login) of
{error, Reason} ->
?LOG(error, "[Mnesia] do_check_acl error: ~p~n", [Reason]),
[];
Re -> Re
end.
%% @doc Remove acl
-spec(remove_acl(binary(), binary()) -> ok | {error, any()}).
remove_acl(Login, Topic) ->
[ ok = mnesia:dirty_delete_object(emqx_acl, #emqx_acl{login = Login, topic = Topic, action = Action, allow = Allow}) || [Action, Allow] <- ets:select(emqx_acl, [{{emqx_acl, Login, Topic,'$1','$2'}, [], ['$$']}])],
ok.
%% @doc All logins
-spec(all_acls() -> list()).
all_acls() -> mnesia:dirty_all_keys(emqx_acl).
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, hash_type, sha256),
emqx_passwd:hash(HashType, Password).
HashType = application:get_env(emqx_auth_mnesia, password_hash, sha256),
SaltBin = salt(),
<<SaltBin/binary, (hash(Password, SaltBin, HashType))/binary>>.
hash(undefined, SaltBin, HashType) ->
hash(<<>>, SaltBin, HashType);
hash(Password, SaltBin, HashType) ->
emqx_passwd:hash(HashType, <<SaltBin/binary, Password/binary>>).
salt() ->
rand:seed(exsplus, erlang:timestamp()),
Salt = rand:uniform(16#ffffffff), <<Salt:32>>.
%%--------------------------------------------------------------------
%% Auth APIs
%% Auth Clientid Cli
%%--------------------------------------------------------------------
%% User
auth_cli(["add", Login, Password, IsSuperuser]) ->
case add_user(iolist_to_binary(Login), iolist_to_binary(Password), IsSuperuser) of
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_cli(["update", Login, NewPassword, IsSuperuser]) ->
case update_user(iolist_to_binary(Login), iolist_to_binary(NewPassword), IsSuperuser) of
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_cli(["del", Login]) ->
case remove_user(iolist_to_binary(Login)) of
auth_clientid_cli(["del", 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_cli(["show", P]) ->
[emqx_ctl:print("User(login = ~p is_super = ~p)~n", [Login, IsSuperuser])
|| {_, Login, _Password, IsSuperuser} <- lookup_user(iolist_to_binary(P))];
auth_clientid_cli(_) ->
emqx_ctl:usage([{"clientid list", "List clientid auth rules"},
{"clientid add <Username> <Password>", "Add clientid auth rule"},
{"clientid update <Username> <NewPassword>", "Update clientid auth rule"},
{"clientid del <Username>", "Delete clientid auth rule"}]).
auth_cli(["list"]) ->
[emqx_ctl:print("User(login = ~p)~n",[E])
|| E <- all_users()];
%%--------------------------------------------------------------------
%% Auth Username Cli
%%--------------------------------------------------------------------
auth_cli(_) ->
emqx_ctl:usage([{"mqtt-user add <Login> <Password> <IsSuper>", "Add user"},
{"mqtt-user update <Login> <NewPassword> <IsSuper>", "Update user"},
{"mqtt-user delete <Login>", "Delete user"},
{"mqtt-user show <Login>", "Lookup user detail"},
{"mqtt-user list", "List all users"}]).
auth_username_cli(["list"]) ->
[emqx_ctl:print("~s~n", [Username]) || {?TABLE, {username, Username}, _Password, _CreatedAt}<- all_users(username)];
%% Acl
acl_cli(["add", Login, Topic, Action, Allow]) ->
case add_acl(iolist_to_binary(Login), iolist_to_binary(Topic), iolist_to_binary(Action), Allow) of
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;
acl_cli(["del", Login, Topic])->
case remove_acl(iolist_to_binary(Login), iolist_to_binary(Topic)) of
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;
acl_cli(["show", P]) ->
[emqx_ctl:print("Acl(login = ~p topic = ~p action = ~p allow = ~p)~n",[Login, Topic, Action, Allow])
|| {_, Login, Topic, Action, Allow} <- lookup_acl(iolist_to_binary(P)) ];
auth_username_cli(["del", 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;
acl_cli(["list"]) ->
[emqx_ctl:print("Acl(login = ~p)~n",[E])
|| E <- all_acls() ];
acl_cli(_) ->
emqx_ctl:usage([{"mqtt-acl add <Login> <Topic> <Action> <Allow>", "Add acl"},
{"mqtt-acl show <Login>", "Lookup acl detail"},
{"mqtt-acl del <Login>", "Delete acl"},
{"mqtt-acl list","List all acls"}]).
auth_username_cli(_) ->
emqx_ctl:usage([{"users list", "List username auth rules"},
{"users add <Username> <Password>", "Add username auth rule"},
{"users update <Username> <NewPassword>", "Update username auth rule"},
{"users del <Username>", "Delete username auth rule"}]).

View File

@ -76,154 +76,41 @@ set_special_configs(_App) ->
t_management(_Config) ->
clean_all_acls(),
?assertEqual("Acl with Mnesia", emqx_acl_mnesia:description()),
?assertEqual([], emqx_auth_mnesia_cli:all_acls()),
?assertEqual([], emqx_acl_mnesia_cli:all_acls()),
ok = emqx_auth_mnesia_cli:add_acl(<<"test_username">>, <<"Topic/A">>, <<"sub">>, true),
ok = emqx_auth_mnesia_cli:add_acl(<<"test_username">>, <<"Topic/B">>, <<"pub">>, true),
ok = emqx_auth_mnesia_cli:add_acl(<<"test_username">>, <<"Topic/C">>, <<"pubsub">>, true),
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),
?assertEqual([{emqx_acl,<<"test_username">>,<<"Topic/A">>,<<"sub">>, true},
{emqx_acl,<<"test_username">>,<<"Topic/B">>,<<"pub">>, true},
{emqx_acl,<<"test_username">>,<<"Topic/C">>,<<"pubsub">>, true}],emqx_auth_mnesia_cli:lookup_acl(<<"test_username">>)),
ok = emqx_auth_mnesia_cli:remove_acl(<<"test_username">>, <<"Topic/A">>),
?assertEqual([{emqx_acl,<<"test_username">>,<<"Topic/B">>,<<"pub">>, true},
{emqx_acl,<<"test_username">>,<<"Topic/C">>,<<"pubsub">>, true}], emqx_auth_mnesia_cli:lookup_acl(<<"test_username">>)),
ok = emqx_auth_mnesia_cli:add_acl(<<"$all">>, <<"Topic/A">>, <<"sub">>, true),
ok = emqx_auth_mnesia_cli:add_acl(<<"$all">>, <<"Topic/B">>, <<"pub">>, true),
ok = emqx_auth_mnesia_cli:add_acl(<<"$all">>, <<"Topic/C">>, <<"pubsub">>, true),
?assertEqual([{emqx_acl,<<"$all">>,<<"Topic/A">>,<<"sub">>, true},
{emqx_acl,<<"$all">>,<<"Topic/B">>,<<"pub">>, true},
{emqx_acl,<<"$all">>,<<"Topic/C">>,<<"pubsub">>, true}],emqx_auth_mnesia_cli:lookup_acl(<<"$all">>)),
ok = emqx_auth_mnesia_cli:remove_acl(<<"$all">>, <<"Topic/A">>),
?assertEqual([{emqx_acl,<<"$all">>,<<"Topic/B">>,<<"pub">>, true},
{emqx_acl,<<"$all">>,<<"Topic/C">>,<<"pubsub">>, true}], emqx_auth_mnesia_cli:lookup_acl(<<"$all">>)).
t_check_acl_as_clientid(_) ->
clean_all_acls(),
emqx_modules:load_module(emqx_mod_acl_internal, false),
?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(1, length(emqx_acl_mnesia_cli:lookup_acl(all))),
?assertEqual(5, length(emqx_acl_mnesia_cli:all_acls())),
User1 = #{zone => external, clientid => <<"test_clientid">>},
User2 = #{zone => external, clientid => <<"no_exist">>},
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">>),
ok = emqx_auth_mnesia_cli:add_acl(<<"test_clientid">>, <<"#">>, <<"sub">>, false),
ok = emqx_auth_mnesia_cli:add_acl(<<"test_clientid">>, <<"+/A">>, <<"pub">>, false),
ok = emqx_auth_mnesia_cli:add_acl(<<"test_clientid">>, <<"Topic/A/B">>, <<"pubsub">>, true),
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({username, <<"test_username">>}, <<"topic/%u">>),
ok = emqx_acl_mnesia_cli:remove_acl({username, <<"test_username">>}, <<"topic/+">>),
ok = emqx_acl_mnesia_cli:remove_acl(all, <<"#">>),
deny = emqx_access_control:check_acl(User1, subscribe, <<"Any">>),
deny = emqx_access_control:check_acl(User1, publish, <<"Any/A">>),
allow = emqx_access_control:check_acl(User1, publish, <<"Any/C">>),
allow = emqx_access_control:check_acl(User1, publish, <<"Topic/A/B">>),
?assertEqual([], emqx_acl_mnesia_cli:all_acls()).
allow = emqx_access_control:check_acl(User2, subscribe, <<"Topic/C">>),
allow = emqx_access_control:check_acl(User2, publish, <<"Topic/D">>).
t_check_acl_as_username(_Config) ->
clean_all_acls(),
emqx_modules:load_module(emqx_mod_acl_internal, false),
User1 = #{zone => external, username => <<"test_username">>},
User2 = #{zone => external, username => <<"no_exist">>},
ok = emqx_auth_mnesia_cli:add_acl(<<"test_username">>, <<"Topic/A">>, <<"sub">>, true),
ok = emqx_auth_mnesia_cli:add_acl(<<"test_username">>, <<"Topic/B">>, <<"pub">>, true),
ok = emqx_auth_mnesia_cli:add_acl(<<"test_username">>, <<"Topic/A/B">>, <<"pubsub">>, false),
allow = emqx_access_control:check_acl(User1, subscribe, <<"Topic/A">>),
allow = emqx_access_control:check_acl(User1, subscribe, <<"Topic/B">>),
deny = emqx_access_control:check_acl(User1, subscribe, <<"Topic/A/B">>),
allow = emqx_access_control:check_acl(User1, publish, <<"Topic/A">>),
allow = emqx_access_control:check_acl(User1, publish, <<"Topic/B">>),
deny = emqx_access_control:check_acl(User1, publish, <<"Topic/A/B">>),
allow = emqx_access_control:check_acl(User2, subscribe, <<"Topic/C">>),
allow = emqx_access_control:check_acl(User2, publish, <<"Topic/D">>).
t_check_acl_as_all(_) ->
clean_all_acls(),
emqx_modules:load_module(emqx_mod_acl_internal, false),
ok = emqx_auth_mnesia_cli:add_acl(<<"$all">>, <<"Topic/A">>, <<"sub">>, false),
ok = emqx_auth_mnesia_cli:add_acl(<<"$all">>, <<"Topic/B">>, <<"pub">>, false),
ok = emqx_auth_mnesia_cli:add_acl(<<"$all">>, <<"Topic/A/B">>, <<"pubsub">>, true),
User1 = #{zone => external, username => <<"test_username">>},
User2 = #{zone => external, username => <<"no_exist">>},
ok = emqx_auth_mnesia_cli:add_acl(<<"test_username">>, <<"Topic/A">>, <<"sub">>, true),
ok = emqx_auth_mnesia_cli:add_acl(<<"test_username">>, <<"Topic/B">>, <<"pub">>, true),
ok = emqx_auth_mnesia_cli:add_acl(<<"test_username">>, <<"Topic/A/B">>, <<"pubsub">>, false),
allow = emqx_access_control:check_acl(User1, subscribe, <<"Topic/A">>),
allow = emqx_access_control:check_acl(User1, subscribe, <<"Topic/B">>),
deny = emqx_access_control:check_acl(User1, subscribe, <<"Topic/A/B">>),
allow = emqx_access_control:check_acl(User1, publish, <<"Topic/A">>),
allow = emqx_access_control:check_acl(User1, publish, <<"Topic/B">>),
deny = emqx_access_control:check_acl(User1, publish, <<"Topic/A/B">>),
deny = emqx_access_control:check_acl(User2, subscribe, <<"Topic/A">>),
deny = emqx_access_control:check_acl(User2, publish, <<"Topic/B">>),
allow = emqx_access_control:check_acl(User2, subscribe, <<"Topic/A/B">>),
allow = emqx_access_control:check_acl(User2, publish, <<"Topic/A/B">>),
allow = emqx_access_control:check_acl(User2, subscribe, <<"Topic/C">>),
allow = emqx_access_control:check_acl(User2, publish, <<"Topic/D">>).
t_rest_api(_Config) ->
clean_all_acls(),
{ok, Result} = request_http_rest_list(),
[] = get_http_data(Result),
Params = #{<<"login">> => <<"test_username">>, <<"topic">> => <<"Topic/A">>, <<"action">> => <<"pubsub">>, <<"allow">> => true},
{ok, _} = request_http_rest_add(Params),
{ok, Result1} = request_http_rest_lookup(<<"test_username">>),
#{<<"login">> := <<"test_username">>, <<"topic">> := <<"Topic/A">>, <<"action">> := <<"pubsub">>, <<"allow">> := true} = get_http_data(Result1),
Params1 = [
#{<<"login">> => <<"$all">>, <<"topic">> => <<"+/A">>, <<"action">> => <<"pub">>, <<"allow">> => true},
#{<<"login">> => <<"test_username">>, <<"topic">> => <<"+/A">>, <<"action">> => <<"pub">>, <<"allow">> => true},
#{<<"login">> => <<"test_username/1">>, <<"topic">> => <<"#">>, <<"action">> => <<"sub">>, <<"allow">> => true},
#{<<"login">> => <<"test_username/2">>, <<"topic">> => <<"+/A">>, <<"action">> => <<"error_format">>, <<"allow">> => true}
],
{ok, Result2} = request_http_rest_add(Params1),
#{
<<"$all">> := <<"ok">>,
<<"test_username">> := <<"ok">>,
<<"test_username/1">> := <<"ok">>,
<<"test_username/2">> := <<"{error,action}">>
} = get_http_data(Result2),
{ok, Result3} = request_http_rest_lookup(<<"test_username">>),
[#{<<"login">> := <<"test_username">>, <<"topic">> := <<"+/A">>, <<"action">> := <<"pub">>, <<"allow">> := true},
#{<<"login">> := <<"test_username">>, <<"topic">> := <<"Topic/A">>, <<"action">> := <<"pubsub">>, <<"allow">> := true}]
= get_http_data(Result3),
{ok, Result4} = request_http_rest_lookup(<<"$all">>),
#{<<"login">> := <<"$all">>, <<"topic">> := <<"+/A">>, <<"action">> := <<"pub">>, <<"allow">> := true}
= get_http_data(Result4),
{ok, _} = request_http_rest_delete(<<"$all">>, <<"+/A">>),
{ok, _} = request_http_rest_delete(<<"test_username">>, <<"+/A">>),
{ok, _} = request_http_rest_delete(<<"test_username">>, <<"Topic/A">>),
{ok, _} = request_http_rest_delete(<<"test_username/1">>, <<"#">>),
{ok, Result5} = request_http_rest_list(),
[] = get_http_data(Result5).
t_run_command(_) ->
clean_all_acls(),
?assertEqual(ok, emqx_ctl:run_command(["mqtt-acl", "add", "TestUser", "Topic/A", "sub", true])),
?assertEqual([{emqx_acl,<<"TestUser">>,<<"Topic/A">>,<<"sub">>, true}],emqx_auth_mnesia_cli:lookup_acl(<<"TestUser">>)),
?assertEqual(ok, emqx_ctl:run_command(["mqtt-acl", "del", "TestUser", "Topic/A"])),
?assertEqual([],emqx_auth_mnesia_cli:lookup_acl(<<"TestUser">>)),
?assertEqual(ok, emqx_ctl:run_command(["mqtt-acl", "show", "TestUser"])),
?assertEqual(ok, emqx_ctl:run_command(["mqtt-acl", "list"])),
?assertEqual(ok, emqx_ctl:run_command(["mqtt-acl"])).
t_cli(_) ->
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),
@ -231,18 +118,67 @@ t_cli(_) ->
meck:expect(emqx_ctl, usage, fun(Cmd, Descr) -> emqx_ctl:format_usage(Cmd, Descr) end),
clean_all_acls(),
?assertMatch({match, _}, re:run(emqx_auth_mnesia_cli:acl_cli(["add", "TestUser", "Topic/A", "sub", true]), "ok")),
?assertMatch(["Acl(login = <<\"TestUser\">> topic = <<\"Topic/A\">> action = <<\"sub\">> allow = true)\n"], emqx_auth_mnesia_cli:acl_cli(["show", "TestUser"])),
?assertMatch(["Acl(login = <<\"TestUser\">>)\n"], emqx_auth_mnesia_cli:acl_cli(["list"])),
?assertMatch({match, _}, re:run(emqx_auth_mnesia_cli:acl_cli(["del", "TestUser", "Topic/A"]), "ok")),
?assertMatch([], emqx_auth_mnesia_cli:acl_cli(["show", "TestUser"])),
?assertMatch([], emqx_auth_mnesia_cli:acl_cli(["list"])),
?assertEqual(0, length(emqx_acl_mnesia_cli:cli(["list"]))),
?assertMatch({match, _}, re:run(emqx_auth_mnesia_cli:acl_cli([]), "mqtt-acl")),
emqx_acl_mnesia_cli:cli(["add", "clientid", "test_clientid", "topic/A", "pub", "allow"]),
?assertMatch(["Acl(clientid = <<\"test_clientid\">> topic = <<\"topic/A\">> action = pub access = allow)\n"], emqx_acl_mnesia_cli:cli(["show", "clientid", "test_clientid"])),
?assertMatch(["Acl(clientid = <<\"test_clientid\">> topic = <<\"topic/A\">> action = pub access = allow)\n"], emqx_acl_mnesia_cli:cli(["list", "clientid"])),
emqx_acl_mnesia_cli:cli(["add", "username", "test_username", "topic/B", "sub", "deny"]),
?assertMatch(["Acl(username = <<\"test_username\">> topic = <<\"topic/B\">> action = sub access = deny)\n"], emqx_acl_mnesia_cli:cli(["show", "username", "test_username"])),
?assertMatch(["Acl(username = <<\"test_username\">> topic = <<\"topic/B\">> action = sub access = deny)\n"], emqx_acl_mnesia_cli:cli(["list", "username"])),
emqx_acl_mnesia_cli:cli(["add", "_all", "#", "pubsub", "deny"]),
?assertMatch(["Acl($all topic = <<\"#\">> action = pubsub access = deny)\n"], emqx_acl_mnesia_cli:cli(["list", "_all"])),
?assertEqual(3, 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(3, 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(3, 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(3, 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
%%------------------------------------------------------------------------------
@ -255,22 +191,22 @@ clean_all_acls() ->
%% HTTP Request
%%--------------------------------------------------------------------
request_http_rest_list() ->
request_api(get, uri(), default_auth_header()).
request_http_rest_list(Path) ->
request_api(get, uri(Path), default_auth_header()).
request_http_rest_lookup(Login) ->
request_api(get, uri([Login]), default_auth_header()).
request_http_rest_lookup(Path) ->
request_api(get, uri(Path), default_auth_header()).
request_http_rest_add(Params) ->
request_api(post, uri(), [], default_auth_header(), Params).
request_http_rest_add(Path, Params) ->
request_api(post, uri(Path), [], default_auth_header(), Params).
request_http_rest_delete(Login, Topic) ->
request_api(delete, uri([Login, Topic]), default_auth_header()).
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, "mqtt_acl"| NParts]).
?HOST ++ filename:join([?BASE_PATH, ?API_VERSION, "acl"| NParts]).
%% @private
b2l(B) when is_binary(B) ->

View File

@ -2,10 +2,7 @@
%% Copyright (c) 2020 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
%% 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,
@ -33,6 +30,12 @@
-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).
@ -81,143 +84,166 @@ set_special_configs(_App) ->
%% Testcases
%%------------------------------------------------------------------------------
t_check_as_username(_Config) ->
t_management(_Config) ->
clean_all_users(),
ok = emqx_auth_mnesia_cli:add_user(<<"test_username">>, <<"password">>, true),
{error, existed} = emqx_auth_mnesia_cli:add_user(<<"test_username">>, <<"password">>, true),
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}, _Password, _InterTime}], emqx_auth_mnesia_cli:all_users(username)),
ok = emqx_auth_mnesia_cli:update_user(<<"test_username">>, <<"new_password">>, false),
{error,noexisted} = emqx_auth_mnesia_cli:update_user(<<"no_existed_user">>, <<"password">>, true),
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}, _Password, _InterTime}], emqx_auth_mnesia_cli:all_users(clientid)),
[<<"test_username">>] = emqx_auth_mnesia_cli:all_users(),
[{emqx_user, <<"test_username">>, _HashedPass, false}] =
emqx_auth_mnesia_cli:lookup_user(<<"test_username">>),
?assertEqual(2,length(emqx_auth_mnesia_cli:all_users())),
User1 = #{username => <<"test_username">>,
password => <<"new_password">>,
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}, _Password, _InterTime}], emqx_auth_mnesia_cli:lookup_user({username, ?USERNAME})),
?assertMatch([{?TABLE, {clientid, ?CLIENTID}, _Password, _InterTime}], emqx_auth_mnesia_cli:lookup_user({clientid, ?CLIENTID})),
User1 = #{username => ?USERNAME,
clientid => undefined,
password => ?NPASSWORD,
zone => external},
{ok, #{is_superuser := false,
auth_result := success,
{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(<<"test_username">>),
ok = emqx_auth_mnesia_cli:remove_user({username,?USERNAME}),
{ok, #{auth_result := success,
anonymous := true }} = emqx_access_control:authenticate(User1).
anonymous := true }} = emqx_access_control:authenticate(User1),
t_check_as_clientid(_Config) ->
clean_all_users(),
ok = emqx_auth_mnesia_cli:add_user(<<"test_clientid">>, <<"password">>, false),
{error, existed} = emqx_auth_mnesia_cli:add_user(<<"test_clientid">>, <<"password">>, false),
ok = emqx_auth_mnesia_cli:update_user(<<"test_clientid">>, <<"new_password">>, true),
{error,noexisted} = emqx_auth_mnesia_cli:update_user(<<"no_existed_user">>, <<"password">>, true),
[<<"test_clientid">>] = emqx_auth_mnesia_cli:all_users(),
[{emqx_user, <<"test_clientid">>, _HashedPass, true}] =
emqx_auth_mnesia_cli:lookup_user(<<"test_clientid">>),
User1 = #{clientid => <<"test_clientid">>,
password => <<"new_password">>,
User2 = #{clientid => ?CLIENTID,
password => ?NPASSWORD,
zone => external},
{ok, #{is_superuser := true,
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(<<"test_clientid">>),
{ok, #{auth_result := success,
anonymous := true }} = emqx_access_control:authenticate(User1).
anonymous := false}} = emqx_access_control:authenticate(User2),
t_rest_api(_Config) ->
{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(),
{ok, Result1} = request_http_rest_list(),
HashType = application:get_env(emqx_auth_mnesia, password_hash, sha256),
emqx_auth_mnesia_cli:auth_clientid_cli(["add", ?CLIENTID, ?PASSWORD]),
[{_, {clientid, ?CLIENTID}, <<Salt:4/binary, Hash/binary>>, _}] = emqx_auth_mnesia_cli:lookup_user({clientid, ?CLIENTID}),
?assertEqual(Hash, emqx_passwd:hash(HashType, <<Salt/binary, ?PASSWORD/binary>>)),
emqx_auth_mnesia_cli:auth_clientid_cli(["update", ?CLIENTID, ?NPASSWORD]),
[{_, {clientid, ?CLIENTID}, <<Salt1:4/binary, Hash1/binary>>, _}] = emqx_auth_mnesia_cli:lookup_user({clientid, ?CLIENTID}),
?assertEqual(Hash1, emqx_passwd:hash(HashType, <<Salt1/binary, ?NPASSWORD/binary>>)),
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}, <<Salt:4/binary, Hash/binary>>, _}] = emqx_auth_mnesia_cli:lookup_user({username, ?USERNAME}),
?assertEqual(Hash, emqx_passwd:hash(HashType, <<Salt/binary, ?PASSWORD/binary>>)),
emqx_auth_mnesia_cli:auth_username_cli(["update", ?USERNAME, ?NPASSWORD]),
[{_, {username, ?USERNAME}, <<Salt1:4/binary, Hash1/binary>>, _}] = emqx_auth_mnesia_cli:lookup_user({username, ?USERNAME}),
?assertEqual(Hash1, emqx_passwd:hash(HashType, <<Salt1/binary, ?NPASSWORD/binary>>)),
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),
Params = #{<<"login">> => <<"test_username">>, <<"password">> => <<"password">>, <<"is_superuser">> => true},
{ok, _} = request_http_rest_add(Params),
Params1 = #{<<"clientid">> => ?CLIENTID, <<"password">> => ?PASSWORD},
{ok, _} = request_http_rest_add(["auth_clientid"], Params1),
Params1 = [
#{<<"login">> => <<"test_username">>, <<"password">> => <<"password">>, <<"is_superuser">> => true},
#{<<"login">> => <<"test_username/1">>, <<"password">> => <<"password">>, <<"is_superuser">> => error_format},
#{<<"login">> => <<"test_username/2">>, <<"password">> => <<"password">>, <<"is_superuser">> => true}
Params2 = #{<<"clientid">> => ?CLIENTID, <<"password">> => ?NPASSWORD},
{ok, _} = request_http_rest_update(["auth_clientid/" ++ binary_to_list(?CLIENTID)], Params2),
{ok, Result2} = request_http_rest_lookup(["auth_clientid/" ++ binary_to_list(?CLIENTID)]),
?assertMatch(#{<<"clientid">> := ?CLIENTID}, get_http_data(Result2)),
Params3 = [ #{<<"clientid">> => ?CLIENTID, <<"password">> => ?PASSWORD}
, #{<<"clientid">> => <<"clientid1">>, <<"password">> => ?PASSWORD}
, #{<<"clientid">> => <<"clientid2">>, <<"password">> => ?PASSWORD}
],
{ok, Result2} = request_http_rest_add(Params1),
#{
<<"test_username">> := <<"{error,existed}">>,
<<"test_username/1">> := <<"{error,is_superuser}">>,
<<"test_username/2">> := <<"ok">>
} = get_http_data(Result2),
{ok, Result3} = request_http_rest_add(["auth_clientid"], Params3),
?assertMatch(#{ ?CLIENTID := <<"{error,existed}">>
, <<"clientid1">> := <<"ok">>
, <<"clientid2">> := <<"ok">>
}, get_http_data(Result3)),
{ok, Result3} = request_http_rest_lookup(<<"test_username">>),
#{<<"login">> := <<"test_username">>, <<"is_superuser">> := true} = get_http_data(Result3),
{ok, Result4} = request_http_rest_list(["auth_clientid"]),
?assertEqual(3, length(get_http_data(Result4))),
{ok, _} = request_http_rest_update(<<"test_username">>, <<"new_password">>, error_format),
{ok, _} = request_http_rest_update(<<"error_username">>, <<"new_password">>, false),
{ok, _} = request_http_rest_update(<<"test_username">>, <<"new_password">>, false),
{ok, Result4} = request_http_rest_lookup(<<"test_username">>),
#{<<"login">> := <<"test_username">>, <<"is_superuser">> := false} = get_http_data(Result4),
User1 = #{username => <<"test_username">>,
password => <<"new_password">>,
zone => external},
{ok, #{is_superuser := false,
auth_result := success,
anonymous := false}} = emqx_access_control:authenticate(User1),
{ok, _} = request_http_rest_delete(<<"test_username">>),
{ok, #{auth_result := success,
anonymous := true }} = emqx_access_control:authenticate(User1).
t_run_command(_) ->
clean_all_users(),
?assertEqual(ok, emqx_ctl:run_command(["mqtt-user", "add", "TestUser", "Password", false])),
?assertMatch([{emqx_user, <<"TestUser">>, _, false}], emqx_auth_mnesia_cli:lookup_user(<<"TestUser">>)),
?assertEqual(ok, emqx_ctl:run_command(["mqtt-user", "update", "TestUser", "NewPassword", true])),
?assertMatch([{emqx_user, <<"TestUser">>, _, true}], emqx_auth_mnesia_cli:lookup_user(<<"TestUser">>)),
?assertEqual(ok, emqx_ctl:run_command(["mqtt-user", "del", "TestUser"])),
?assertMatch([], emqx_auth_mnesia_cli:lookup_user(<<"TestUser">>)),
?assertEqual(ok, emqx_ctl:run_command(["mqtt-user", "show", "TestUser"])),
?assertEqual(ok, emqx_ctl:run_command(["mqtt-user", "list"])),
?assertEqual(ok, emqx_ctl:run_command(["mqtt-user"])).
t_cli(_) ->
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),
{ok, _} = request_http_rest_delete(["auth_clientid/" ++ binary_to_list(?CLIENTID)]),
{ok, Result5} = request_http_rest_lookup(["auth_clientid/" ++ binary_to_list(?CLIENTID)]),
?assertMatch(#{}, get_http_data(Result5)).
t_username_rest_api(_Config) ->
clean_all_users(),
?assertMatch({match, _}, re:run(emqx_auth_mnesia_cli:auth_cli(["add", "TestUser", "Password", true]), "ok")),
?assertMatch({match, _}, re:run(emqx_auth_mnesia_cli:auth_cli(["add", "TestUser", "Password", true]), "Error")),
{ok, Result1} = request_http_rest_list(["auth_username"]),
[] = get_http_data(Result1),
?assertMatch({match, _}, re:run(emqx_auth_mnesia_cli:auth_cli(["update", "NoExisted", "Password", false]), "Error")),
?assertMatch({match, _}, re:run(emqx_auth_mnesia_cli:auth_cli(["update", "TestUser", "Password", false]), "ok")),
Params1 = #{<<"username">> => ?USERNAME, <<"password">> => ?PASSWORD},
{ok, _} = request_http_rest_add(["auth_username"], Params1),
?assertMatch(["User(login = <<\"TestUser\">> is_super = false)\n"], emqx_auth_mnesia_cli:auth_cli(["show", "TestUser"])),
?assertMatch(["User(login = <<\"TestUser\">>)\n"], emqx_auth_mnesia_cli:auth_cli(["list"])),
Params2 = #{<<"username">> => ?USERNAME, <<"password">> => ?NPASSWORD},
{ok, _} = request_http_rest_update(["auth_username/" ++ binary_to_list(?USERNAME)], Params2),
?assertMatch({match, _}, re:run(emqx_auth_mnesia_cli:auth_cli(["del", "TestUser"]), "ok")),
?assertMatch([], emqx_auth_mnesia_cli:auth_cli(["show", "TestUser"])),
?assertMatch([], emqx_auth_mnesia_cli:auth_cli(["list"])),
{ok, Result2} = request_http_rest_lookup(["auth_username/" ++ binary_to_list(?USERNAME)]),
?assertMatch(#{<<"username">> := ?USERNAME}, get_http_data(Result2)),
?assertMatch({match, _}, re:run(emqx_auth_mnesia_cli:auth_cli([]), "mqtt-user")),
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)),
meck:unload(emqx_ctl).
{ok, Result4} = request_http_rest_list(["auth_username"]),
?assertEqual(3, length(get_http_data(Result4))),
{ok, _} = request_http_rest_delete(["auth_username/" ++ binary_to_list(?USERNAME)]),
{ok, Result5} = request_http_rest_lookup(["auth_username/" ++ binary_to_list(?USERNAME)]),
?assertMatch(#{}, get_http_data(Result5)).
%%------------------------------------------------------------------------------
%% Helpers
@ -231,18 +257,17 @@ clean_all_users() ->
%% HTTP Request
%%--------------------------------------------------------------------
request_http_rest_list() ->
request_api(get, uri(), default_auth_header()).
request_http_rest_list(Path) ->
request_api(get, uri(Path), default_auth_header()).
request_http_rest_lookup(Login) ->
request_api(get, uri([Login]), default_auth_header()).
request_http_rest_lookup(Path) ->
request_api(get, uri([Path]), default_auth_header()).
request_http_rest_add(Params) ->
request_api(post, uri(), [], default_auth_header(), Params).
request_http_rest_add(Path, Params) ->
request_api(post, uri(Path), [], default_auth_header(), Params).
request_http_rest_update(Login, Password, IsSuperuser) ->
Params = #{<<"password">> => Password, <<"is_superuser">> => IsSuperuser},
request_api(put, uri([Login]), [], 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()).
@ -250,7 +275,7 @@ request_http_rest_delete(Login) ->
uri() -> uri([]).
uri(Parts) when is_list(Parts) ->
NParts = [b2l(E) || E <- Parts],
?HOST ++ filename:join([?BASE_PATH, ?API_VERSION, "mqtt_user"| NParts]).
?HOST ++ filename:join([?BASE_PATH, ?API_VERSION | NParts]).
%% @private
b2l(B) when is_binary(B) ->

View File

@ -1,59 +0,0 @@
name: Run test cases
on: [push, pull_request]
jobs:
run_test_cases:
runs-on: ubuntu-latest
strategy:
matrix:
mongo_tag:
- 3
- 4
network_type:
- ipv4
- ipv6
connect_type:
- ssl
- tcp
steps:
- name: install docker-compose
run: |
sudo curl -L "https://github.com/docker/compose/releases/download/1.25.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
- uses: actions/checkout@v1
- name: run test cases
env:
MONGO_TAG: ${{ matrix.mongo_tag }}
NETWORK_TYPE: ${{ matrix.network_type }}
CONNECT_TYPE: ${{ matrix.connect_type }}
run: |
set -e -u -x
if [ "$NETWORK_TYPE" = "ipv6" ];then docker network create --driver bridge --ipv6 --subnet fd15:555::/64 tests_emqx_bridge --attachable; fi
if [ "$CONNECT_TYPE" = "ssl" ]; then
docker-compose -f ./docker-compose-ssl.yml -p tests up -d
docker exec -i $(docker ps -a -f name=tests_erlang_1 -q) sh -c "echo 'auth.mongo.ssl = true' >> /emqx_auth_mongo/etc/emqx_auth_mongo.conf"
docker exec -i $(docker ps -a -f name=tests_erlang_1 -q) sh -c "echo 'auth.mongo.ssl_opts.cacertfile = /emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/ca.pem' >> /emqx_auth_mongo/etc/emqx_auth_mongo.conf"
docker exec -i $(docker ps -a -f name=tests_erlang_1 -q) sh -c "echo 'auth.mongo.ssl_opts.certfile = /emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/client-cert.pem' >> /emqx_auth_mongo/etc/emqx_auth_mongo.conf"
docker exec -i $(docker ps -a -f name=tests_erlang_1 -q) sh -c "echo 'auth.mongo.ssl_opts.keyfile = /emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/client-key.pem' >> /emqx_auth_mongo/etc/emqx_auth_mongo.conf"
else
docker-compose -f ./docker-compose.yml -p tests up -d
fi
if [ "$NETWORK_TYPE" != "ipv6" ];then
docker exec -i $(docker ps -a -f name=tests_erlang_1 -q) sh -c "sed -i '/auth.mongo.server/c auth.mongo.server = mongo_server:27017' /emqx_auth_mongo/etc/emqx_auth_mongo.conf"
else
ipv6_address=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.GlobalIPv6Address}}{{end}}' $(docker ps -a -f name=tests_mongo_server_1 -q))
docker exec -i $(docker ps -a -f name=tests_erlang_1 -q) sh -c "sed -i '/auth.mongo.server/c auth.mongo.server = $ipv6_address:27017' /emqx_auth_mongo/etc/emqx_auth_mongo.conf"
fi
docker exec -i tests_erlang_1 sh -c "make -C /emqx_auth_mongo xref"
docker exec -i tests_erlang_1 sh -c "make -C /emqx_auth_mongo eunit"
docker exec -i tests_erlang_1 sh -c "make -C /emqx_auth_mongo ct"
docker exec -i tests_erlang_1 sh -c "make -C /emqx_auth_mongo cover"
- uses: actions/upload-artifact@v1
if: failure()
with:
name: logs_mongo${{ matrix.mongo_tag}}_${{ matrix.network_type }}
path: _build/test/logs

View File

@ -1,24 +0,0 @@
%%-*- mode: erlang -*-
%% .app.src.script
RemoveLeadingV =
fun(Tag) ->
case re:run(Tag, "^[v|e]?[0-9]\.[0-9]\.([0-9]|(rc|beta|alpha)\.[0-9])", [{capture, none}]) of
nomatch ->
re:replace(Tag, "/", "-", [{return ,list}]);
_ ->
%% if it is a version number prefixed by 'v' or 'e', then remove it
re:replace(Tag, "[v|e]", "", [{return ,list}])
end
end,
case os:getenv("EMQX_DEPS_DEFAULT_VSN") of
false -> CONFIG; % env var not defined
[] -> CONFIG; % env var set to empty string
Tag ->
[begin
AppConf0 = lists:keystore(vsn, 1, AppConf, {vsn, RemoveLeadingV(Tag)}),
{application, App, AppConf0}
end || Conf = {application, App, AppConf} <- CONFIG]
end.

View File

@ -1,37 +0,0 @@
%%-*-: erlang -*-
{"4.2.3",
[
{"4.2.2", [
{load_module, emqx_auth_mongo_app, brutal_purge, soft_purge, []},
{load_module, emqx_auth_mongo, brutal_purge, soft_purge, []},
{load_module, emqx_acl_mongo, brutal_purge, soft_purge, [emqx_auth_mongo]}
]},
{"4.2.1", [
{load_module, emqx_auth_mongo_app, brutal_purge, soft_purge, []},
{load_module, emqx_auth_mongo, brutal_purge, soft_purge, []},
{load_module, emqx_acl_mongo, brutal_purge, soft_purge, [emqx_auth_mongo]}
]},
{"4.2.0", [
{load_module, emqx_auth_mongo_app, brutal_purge, soft_purge, []},
{load_module, emqx_auth_mongo, brutal_purge, soft_purge, []},
{load_module, emqx_acl_mongo, brutal_purge, soft_purge, [emqx_auth_mongo]}
]}
],
[
{"4.2.2", [
{load_module, emqx_auth_mongo_app, brutal_purge, soft_purge, []},
{load_module, emqx_auth_mongo, brutal_purge, soft_purge, []},
{load_module, emqx_acl_mongo, brutal_purge, soft_purge, [emqx_auth_mongo]}
]},
{"4.2.1", [
{load_module, emqx_auth_mongo_app, brutal_purge, soft_purge, []},
{load_module, emqx_auth_mongo, brutal_purge, soft_purge, []},
{load_module, emqx_acl_mongo, brutal_purge, soft_purge, [emqx_auth_mongo]}
]},
{"4.2.0", [
{load_module, emqx_auth_mongo_app, brutal_purge, soft_purge, []},
{load_module, emqx_auth_mongo, brutal_purge, soft_purge, []},
{load_module, emqx_acl_mongo, brutal_purge, soft_purge, [emqx_auth_mongo]}
]}
]
}.

View File

@ -1,59 +0,0 @@
name: Run test cases
on: [push, pull_request]
jobs:
run_test_cases:
runs-on: ubuntu-latest
strategy:
matrix:
mysql_tag:
- 5.7
- 8
network_type:
- ipv4
- ipv6
connect_type:
- ssl
- tcp
steps:
- name: install docker-compose
run: |
sudo curl -L "https://github.com/docker/compose/releases/download/1.25.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
- uses: actions/checkout@v1
- name: run test cases
env:
MYSQL_TAG: ${{ matrix.mysql_tag }}
NETWORK_TYPE: ${{ matrix.network_type }}
CONNECT_TYPE: ${{ matrix.connect_type }}
run: |
if [ "$NETWORK_TYPE" = "ipv6" ];then docker network create --driver bridge --ipv6 --subnet fd15:555::/64 test_emqx_bridge --attachable; fi
if [ "$CONNECT_TYPE" = "ssl" ]; then
docker-compose -f ./docker-compose-ssl.yml -p test up -d
docker exec -i $(docker ps -a -f name=test_erlang_1 -q) sh -c "echo 'auth.mysql.ssl.cafile = /emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/ca.pem' >> /emqx_auth_mysql/etc/emqx_auth_mysql.conf"
docker exec -i $(docker ps -a -f name=test_erlang_1 -q) sh -c "echo 'auth.mysql.ssl.certfile = /emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/client-cert.pem' >> /emqx_auth_mysql/etc/emqx_auth_mysql.conf"
docker exec -i $(docker ps -a -f name=test_erlang_1 -q) sh -c "echo 'auth.mysql.ssl.keyfile = /emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/client-key.pem' >> /emqx_auth_mysql/etc/emqx_auth_mysql.conf"
else
docker-compose -f ./docker-compose.yml -p test up -d
fi
docker exec -i $(docker ps -a -f name=test_erlang_1 -q) sh -c "echo 'auth.mysql.username = root' >> /emqx_auth_mysql/etc/emqx_auth_mysql.conf "
docker exec -i $(docker ps -a -f name=test_erlang_1 -q) sh -c "echo 'auth.mysql.password = public' >> /emqx_auth_mysql/etc/emqx_auth_mysql.conf"
if [ "$NETWORK_TYPE" != "ipv6" ];then
docker exec -i $(docker ps -a -f name=test_erlang_1 -q) sh -c "sed -i '/auth.mysql.server/c auth.mysql.server = mysql_server:3306' /emqx_auth_mysql/etc/emqx_auth_mysql.conf"
else
ipv6_address=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.GlobalIPv6Address}}{{end}}' $(docker ps -a -f name=test_mysql_server_1 -q))
docker exec -i $(docker ps -a -f name=test_erlang_1 -q) sh -c "sed -i '/auth.mysql.server/c auth.mysql.server = $ipv6_address:3306' /emqx_auth_mysql/etc/emqx_auth_mysql.conf"
fi
docker exec -i $(docker ps -a -f name=test_erlang_1 -q) sh -c "make -C /emqx_auth_mysql xref"
docker exec -i $(docker ps -a -f name=test_erlang_1 -q) sh -c "make -C /emqx_auth_mysql eunit"
docker exec -i $(docker ps -a -f name=test_erlang_1 -q) sh -c "make -C /emqx_auth_mysql ct"
docker exec -i $(docker ps -a -f name=test_erlang_1 -q) sh -c "make -C /emqx_auth_mysql cover"
- uses: actions/upload-artifact@v1
if: failure()
with:
name: logs_mysql${{ matrix.mysql_tag }}_${{ matrix.network_type }}
path: _build/test/logs

View File

@ -1,24 +0,0 @@
%%-*- mode: erlang -*-
%% .app.src.script
RemoveLeadingV =
fun(Tag) ->
case re:run(Tag, "^[v|e]?[0-9]\.[0-9]\.([0-9]|(rc|beta|alpha)\.[0-9])", [{capture, none}]) of
nomatch ->
re:replace(Tag, "/", "-", [{return ,list}]);
_ ->
%% if it is a version number prefixed by 'v' or 'e', then remove it
re:replace(Tag, "[v|e]", "", [{return ,list}])
end
end,
case os:getenv("EMQX_DEPS_DEFAULT_VSN") of
false -> CONFIG; % env var not defined
[] -> CONFIG; % env var set to empty string
Tag ->
[begin
AppConf0 = lists:keystore(vsn, 1, AppConf, {vsn, RemoveLeadingV(Tag)}),
{application, App, AppConf0}
end || Conf = {application, App, AppConf} <- CONFIG]
end.

View File

@ -1,25 +0,0 @@
%% -*-: erlang -*-
{"4.2.3",
[
{"4.2.2", [
{load_module, emqx_auth_mysql_cli, brutal_purge, soft_purge, []}
]},
{"4.2.1", [
{load_module, emqx_auth_mysql_cli, brutal_purge, soft_purge, []}
]},
{"4.2.0", [
{load_module, emqx_auth_mysql_cli, brutal_purge, soft_purge, []}
]}
],
[
{"4.2.2", [
{load_module, emqx_auth_mysql_cli, brutal_purge, soft_purge, []}
]},
{"4.2.1", [
{load_module, emqx_auth_mysql_cli, brutal_purge, soft_purge, []}
]},
{"4.2.0", [
{load_module, emqx_auth_mysql_cli, brutal_purge, soft_purge, []}
]}
]
}.

View File

@ -1,30 +0,0 @@
version: '3'
services:
erlang:
image: erlang:22.3
volumes:
- ../:/emqx_auth_pgsql
networks:
- emqx_bridge
depends_on:
- pgsql_server
tty: true
pgsql_server:
build:
context: ./pgsql
args:
BUILD_FROM: postgres:${PGSQL_TAG}
image: emqx-pgsql
restart: always
environment:
POSTGRES_PASSWORD: public
POSTGRES_USER: root
POSTGRES_DB: mqtt
networks:
- emqx_bridge
networks:
emqx_bridge:
driver: bridge

View File

@ -1,8 +0,0 @@
ARG BUILD_FROM=postgres:11
FROM ${BUILD_FROM}
COPY pg.conf /etc/postgresql/postgresql.conf
COPY server-cert.pem /etc/postgresql/server-cert.pem
COPY server-key.pem /etc/postgresql/server-key.pem
RUN chown -R postgres:postgres /etc/postgresql \
&& chmod 600 /etc/postgresql/*.pem
CMD ["-c", "config_file=/etc/postgresql/postgresql.conf"]

View File

@ -1,51 +0,0 @@
name: Run test cases
on: [push, pull_request]
jobs:
run_test_cases:
runs-on: ubuntu-latest
strategy:
matrix:
pgsql_tag:
- 9
- 10
- 11
- 12
- 13
network_type:
- ipv4
- ipv6
steps:
- name: install docker-compose
run: |
sudo curl -L "https://github.com/docker/compose/releases/download/1.25.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
- uses: actions/checkout@v1
- name: run test cases
env:
PGSQL_TAG: ${{ matrix.pgsql_tag }}
NETWORK_TYPE: ${{ matrix.network_type }}
run: |
set -e -u -x
if [ "$NETWORK_TYPE" = "ipv6" ]; then docker network create --driver bridge --ipv6 --subnet fd15:555::/64 tests_emqx_bridge --attachable; fi
cp test/emqx_auth_pgsql_SUITE_data/* .ci/pgsql/
docker-compose -f .ci/docker-compose.yml -p tests up -d --build
if [ "$NETWORK_TYPE" != "ipv6" ]; then
docker exec -i $(docker ps -a -f name=tests_erlang_1 -q) sh -c "sed -i '/auth.pgsql.server/c auth.pgsql.server = pgsql_server:5432' /emqx_auth_pgsql/etc/emqx_auth_pgsql.conf"
else
ipv6_address=$(docker inspect -f '{{range.NetworkSettings.Networks}}{{.GlobalIPv6Address}}{{end}}' $(docker ps -a -f name=tests_pgsql_server_1 -q))
docker exec -i $(docker ps -a -f name=tests_erlang_1 -q) sh -c "sed -i '/auth.pgsql.server/c auth.pgsql.server = $ipv6_address:5432' /emqx_auth_pgsql/etc/emqx_auth_pgsql.conf"
fi
docker exec -i tests_erlang_1 sh -c "make -C /emqx_auth_pgsql xref"
docker exec -i tests_erlang_1 sh -c "make -C /emqx_auth_pgsql eunit"
docker exec -i tests_erlang_1 sh -c "make -C /emqx_auth_pgsql ct"
docker exec -i tests_erlang_1 sh -c "make -C /emqx_auth_pgsql cover"
- uses: actions/upload-artifact@v1
if: failure()
with:
name: logs_for_pgsql${{ matrix.pgsql_tag }}_{{matrix.network_type}}
path: _build/test/logs

View File

@ -1,24 +0,0 @@
%%-*- mode: erlang -*-
%% .app.src.script
RemoveLeadingV =
fun(Tag) ->
case re:run(Tag, "^[v|e]?[0-9]\.[0-9]\.([0-9]|(rc|beta|alpha)\.[0-9])", [{capture, none}]) of
nomatch ->
re:replace(Tag, "/", "-", [{return ,list}]);
_ ->
%% if it is a version number prefixed by 'v' or 'e', then remove it
re:replace(Tag, "[v|e]", "", [{return ,list}])
end
end,
case os:getenv("EMQX_DEPS_DEFAULT_VSN") of
false -> CONFIG; % env var not defined
[] -> CONFIG; % env var set to empty string
Tag ->
[begin
AppConf0 = lists:keystore(vsn, 1, AppConf, {vsn, RemoveLeadingV(Tag)}),
{application, App, AppConf0}
end || Conf = {application, App, AppConf} <- CONFIG]
end.

View File

@ -1,25 +0,0 @@
%% -*-: erlang -*-
{"4.2.3",
[
{"4.2.2", [
{load_module, emqx_auth_pgsql_cli, brutal_purge, soft_purge, []}
]},
{"4.2.1", [
{load_module, emqx_auth_pgsql_cli, brutal_purge, soft_purge, []}
]},
{"4.2.0", [
{load_module, emqx_auth_pgsql_cli, brutal_purge, soft_purge, []}
]}
],
[
{"4.2.2", [
{load_module, emqx_auth_pgsql_cli, brutal_purge, soft_purge, []}
]},
{"4.2.1", [
{load_module, emqx_auth_pgsql_cli, brutal_purge, soft_purge, []}
]},
{"4.2.0", [
{load_module, emqx_auth_pgsql_cli, brutal_purge, soft_purge, []}
]}
]
}.

View File

@ -1,31 +0,0 @@
version: '3'
services:
erlang:
image: erlang:22.3
volumes:
- ../:/emqx_auth_redis
networks:
- emqx_bridge
depends_on:
- redis_server
tty: true
redis_server:
image: redis:6.0.9
volumes:
- ../test/emqx_auth_redis_SUITE_data/certs:/tls
command:
- redis-server
- "--bind 0.0.0.0 ::"
- --tls-port 6380
- --tls-cert-file /tls/redis.crt
- --tls-key-file /tls/redis.key
- --tls-ca-cert-file /tls/ca.crt
restart: always
networks:
- emqx_bridge
networks:
emqx_bridge:
driver: bridge

View File

@ -1,25 +0,0 @@
version: '3'
services:
erlang:
image: erlang:22.3
volumes:
- ../:/emqx_auth_redis
networks:
- emqx_bridge
depends_on:
- redis_server
tty: true
redis_server:
image: redis:${REDIS_TAG}
command:
- redis-server
- "--bind 0.0.0.0 ::"
restart: always
networks:
- emqx_bridge
networks:
emqx_bridge:
driver: bridge

View File

@ -1,80 +0,0 @@
name: Run test cases
on: [push, pull_request]
jobs:
run_tests_cases:
runs-on: ubuntu-latest
strategy:
matrix:
redis_tag:
- 5.0.9
- 6.0.9
network_type:
- ipv4
- ipv6
connect_type:
- tcp
- tls
steps:
- name: install docker-compose
run: |
sudo curl -L "https://github.com/docker/compose/releases/download/1.25.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
- uses: actions/checkout@v1
- name: setup
if: matrix.connect_type == 'tcp' && matrix.network_type == 'ipv6'
env:
REDIS_TAG: ${{ matrix.redis_tag}}
run: |
set -e -u -x
docker network create --driver bridge --ipv6 --subnet fd15:555::/64 tests_emqx_bridge --attachable;
docker-compose -f ./.ci/docker-compose.yml -p tests up -d
ipv6_address=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.GlobalIPv6Address}}{{end}}' $(docker ps -a -f name=tests_redis_server_1 -q))
sed -i "/auth.redis.server/c auth.redis.server = $ipv6_address:6379" ./etc/emqx_auth_redis.conf
- name: setup
if: matrix.connect_type == 'tcp' && matrix.network_type == 'ipv4'
env:
REDIS_TAG: ${{ matrix.redis_tag}}
run: |
set -e -u -x
docker-compose -f ./.ci/docker-compose.yml -p tests up -d
sed -i '/auth.redis.server/c auth.redis.server = redis_server:6379' ./etc/emqx_auth_redis.conf
- name: setup
if: matrix.connect_type == 'tls' && matrix.network_type == 'ipv6' && matrix.redis_tag == '6.0.9'
run: |
set -e -u -x
docker network create --driver bridge --ipv6 --subnet fd15:555::/64 tests_emqx_bridge --attachable;
docker-compose -f ./.ci/docker-compose-tls.yml -p tests up -d
ipv6_address=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.GlobalIPv6Address}}{{end}}' $(docker ps -a -f name=tests_redis_server_1 -q))
sed -i "/auth.redis.server/c auth.redis.server = $ipv6_address:6380" ./etc/emqx_auth_redis.conf
echo '\n' >> ./etc/emqx_auth_redis.conf
echo 'auth.redis.ssl = on' >> ./etc/emqx_auth_redis.conf
echo 'auth.redis.cafile = /emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/ca.crt' >> ./etc/emqx_auth_redis.conf
echo 'auth.redis.certfile = /emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/redis.crt' >> ./etc/emqx_auth_redis.conf
echo 'auth.redis.keyfile = /emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/redis.key' >> ./etc/emqx_auth_redis.conf
- name: setup
if: matrix.connect_type == 'tls' && matrix.network_type == 'ipv4' && matrix.redis_tag == '6.0.9'
run: |
set -e -u -x
docker-compose -f ./.ci/docker-compose-tls.yml -p tests up -d
sed -i '/auth.redis.server/c auth.redis.server = redis_server:6380' ./etc/emqx_auth_redis.conf
echo '\n' >> ./etc/emqx_auth_redis.conf
echo 'auth.redis.ssl = on' >> ./etc/emqx_auth_redis.conf
echo 'auth.redis.cafile = /emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/ca.crt' >> ./etc/emqx_auth_redis.conf
echo 'auth.redis.certfile = /emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/redis.crt' >> ./etc/emqx_auth_redis.conf
echo 'auth.redis.keyfile = /emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/redis.key' >> ./etc/emqx_auth_redis.conf
- name: run test cases
if: matrix.connect_type == 'tcp' || (matrix.connect_type == 'tls' && matrix.redis_tag == '6.0.9')
run: |
docker exec -i tests_erlang_1 sh -c "make -C /emqx_auth_redis xref"
docker exec -i tests_erlang_1 sh -c "make -C /emqx_auth_redis eunit"
docker exec -i tests_erlang_1 sh -c "make -C /emqx_auth_redis ct"
docker exec -i tests_erlang_1 sh -c "make -C /emqx_auth_redis cover"
- uses: actions/upload-artifact@v1
if: failure()
with:
name: logs_redis${{ matrix.redis_tag}}_${{ matrix.network_type }}_${{ matrix.connect_type }}
path: _build/test/logs

View File

@ -1,24 +0,0 @@
%%-*- mode: erlang -*-
%% .app.src.script
RemoveLeadingV =
fun(Tag) ->
case re:run(Tag, "^[v|e]?[0-9]\.[0-9]\.([0-9]|(rc|beta|alpha)\.[0-9])", [{capture, none}]) of
nomatch ->
re:replace(Tag, "/", "-", [{return ,list}]);
_ ->
%% if it is a version number prefixed by 'v' or 'e', then remove it
re:replace(Tag, "[v|e]", "", [{return ,list}])
end
end,
case os:getenv("EMQX_DEPS_DEFAULT_VSN") of
false -> CONFIG; % env var not defined
[] -> CONFIG; % env var set to empty string
Tag ->
[begin
AppConf0 = lists:keystore(vsn, 1, AppConf, {vsn, RemoveLeadingV(Tag)}),
{application, App, AppConf0}
end || Conf = {application, App, AppConf} <- CONFIG]
end.

View File

@ -1,36 +0,0 @@
{"4.2.3",
[
{"4.2.2", [
{load_module, emqx_auth_redis_cli, brutal_purge, soft_purge, []},
{load_module, emqx_auth_redis_sup, brutal_purge, soft_purge, []}
]
},
{"4.2.1", [
{load_module, emqx_auth_redis_cli, brutal_purge, soft_purge, []},
{load_module, emqx_auth_redis_sup, brutal_purge, soft_purge, []}
]
},
{"4.2.0", [
{load_module, emqx_auth_redis_cli, brutal_purge, soft_purge, []},
{load_module, emqx_auth_redis_sup, brutal_purge, soft_purge, []}
]
}
],
[
{"4.2.2", [
{load_module, emqx_auth_redis_cli, brutal_purge, soft_purge, []},
{load_module, emqx_auth_redis_sup, brutal_purge, soft_purge, []}
]
},
{"4.2.1", [
{load_module, emqx_auth_redis_cli, brutal_purge, soft_purge, []},
{load_module, emqx_auth_redis_sup, brutal_purge, soft_purge, []}
]
},
{"4.2.0", [
{load_module, emqx_auth_redis_cli, brutal_purge, soft_purge, []},
{load_module, emqx_auth_redis_sup, brutal_purge, soft_purge, []}
]
}
]
}.

View File

@ -1,28 +0,0 @@
name: Run test cases
on: [push, pull_request]
jobs:
run_test_cases:
runs-on: ubuntu-latest
container:
image: erlang:22.1
steps:
- uses: actions/checkout@v1
- name: run test cases
run: |
make eunit
make ct
make cover
- uses: actions/upload-artifact@v1
if: always()
with:
name: logs
path: _build/test/logs
- uses: actions/upload-artifact@v1
with:
name: cover
path: _build/test/cover

View File

@ -1,24 +0,0 @@
%%-*- mode: erlang -*-
%% .app.src.script
RemoveLeadingV =
fun(Tag) ->
case re:run(Tag, "^[v|e]?[0-9]\.[0-9]\.([0-9]|(rc|beta|alpha)\.[0-9])", [{capture, none}]) of
nomatch ->
re:replace(Tag, "/", "-", [{return ,list}]);
_ ->
%% if it is a version number prefixed by 'v' or 'e', then remove it
re:replace(Tag, "[v|e]", "", [{return ,list}])
end
end,
case os:getenv("EMQX_DEPS_DEFAULT_VSN") of
false -> CONFIG; % env var not defined
[] -> CONFIG; % env var set to empty string
Tag ->
[begin
AppConf0 = lists:keystore(vsn, 1, AppConf, {vsn, RemoveLeadingV(Tag)}),
{application, App, AppConf0}
end || Conf = {application, App, AppConf} <- CONFIG]
end.

View File

@ -1,32 +0,0 @@
%% -*-: erlang -*-
{"4.2.3",
[
{"4.2.2", [
{load_module, emqx_bridge_mqtt_actions, brutal_purge, soft_purge, []},
{apply, {emqx_rule_engine, load_providers, []}}
]},
{"4.2.1", [
{load_module, emqx_bridge_mqtt_actions, brutal_purge, soft_purge, []},
{apply, {emqx_rule_engine, load_providers, []}}
]},
{"4.2.0", [
{load_module, emqx_bridge_mqtt_actions, brutal_purge, soft_purge, []},
{apply, {emqx_rule_engine, load_providers, []}}
]}
],
[
{"4.2.2", [
{load_module, emqx_bridge_mqtt_actions, brutal_purge, soft_purge, []},
{apply, {emqx_rule_engine, load_providers, []}}
]},
{"4.2.1", [
{load_module, emqx_bridge_mqtt_actions, brutal_purge, soft_purge, []},
{apply, {emqx_rule_engine, load_providers, []}}
]},
{"4.2.0", [
{load_module, emqx_bridge_mqtt_actions, brutal_purge, soft_purge, []},
{apply, {emqx_rule_engine, load_providers, []}}
]}
]
}.

View File

@ -1,29 +0,0 @@
name: Run test cases
on: [push, pull_request]
jobs:
run_test_cases:
runs-on: ubuntu-latest
container:
image: erlang:22.1
steps:
- uses: actions/checkout@v1
- name: run test cases
run: |
make xref
make eunit
make ct
make cover
- uses: actions/upload-artifact@v1
if: always()
with:
name: logs
path: _build/test/logs
- uses: actions/upload-artifact@v1
with:
name: cover
path: _build/test/cover

View File

@ -1,24 +0,0 @@
%%-*- mode: erlang -*-
%% .app.src.script
RemoveLeadingV =
fun(Tag) ->
case re:run(Tag, "^[v|e]?[0-9]\.[0-9]\.([0-9]|(rc|beta|alpha)\.[0-9])", [{capture, none}]) of
nomatch ->
re:replace(Tag, "/", "-", [{return ,list}]);
_ ->
%% if it is a version number prefixed by 'v' or 'e', then remove it
re:replace(Tag, "[v|e]", "", [{return ,list}])
end
end,
case os:getenv("EMQX_DEPS_DEFAULT_VSN") of
false -> CONFIG; % env var not defined
[] -> CONFIG; % env var set to empty string
Tag ->
[begin
AppConf0 = lists:keystore(vsn, 1, AppConf, {vsn, RemoveLeadingV(Tag)}),
{application, App, AppConf0}
end || Conf = {application, App, AppConf} <- CONFIG]
end.

View File

@ -1,29 +0,0 @@
name: Run test cases
on: [push, pull_request]
jobs:
run_test_cases:
runs-on: ubuntu-latest
container:
image: erlang:22.1
steps:
- uses: actions/checkout@v1
- name: run test cases
run: |
make xref
make eunit
make ct
make cover
- uses: actions/upload-artifact@v1
if: always()
with:
name: logs
path: _build/test/logs
- uses: actions/upload-artifact@v1
with:
name: cover
path: _build/test/cover

View File

@ -1,24 +0,0 @@
%%-*- mode: erlang -*-
%% .app.src.script
RemoveLeadingV =
fun(Tag) ->
case re:run(Tag, "^[v|e]?[0-9]\.[0-9]\.([0-9]|(rc|beta|alpha)\.[0-9])", [{capture, none}]) of
nomatch ->
re:replace(Tag, "/", "-", [{return ,list}]);
_ ->
%% if it is a version number prefixed by 'v' or 'e', then remove it
re:replace(Tag, "[v|e]", "", [{return ,list}])
end
end,
case os:getenv("EMQX_DEPS_DEFAULT_VSN") of
false -> CONFIG; % env var not defined
[] -> CONFIG; % env var set to empty string
Tag ->
[begin
AppConf0 = lists:keystore(vsn, 1, AppConf, {vsn, RemoveLeadingV(Tag)}),
{application, App, AppConf0}
end || Conf = {application, App, AppConf} <- CONFIG]
end.

View File

@ -1,18 +0,0 @@
%% -*-: erlang -*-
{"4.2.3",
[
{<<"4.2.*">>,
[
{restart_application, emqx_dashboard},
{apply, {emqx_plugins, load, []}}
]}
],
[
{<<"4.2.*">>,
[
{restart_application, emqx_dashboard},
{apply, {emqx_plugins, load, []}}
]}
]
}.

View File

@ -1,31 +0,0 @@
name: Run test cases
on: [push, pull_request]
jobs:
run_test_cases:
runs-on: ubuntu-latest
container:
image: erlang:22.1
steps:
- uses: actions/checkout@v1
- uses: actions/setup-java@v1
with:
java-version: '8.0.x'
java-package: jdk
- name: run test cases
run: |
make eunit
make ct
make cover
- uses: actions/upload-artifact@v1
if: always()
with:
name: logs
path: _build/test/logs
- uses: actions/upload-artifact@v1
with:
name: cover
path: _build/test/cover

View File

@ -1,38 +0,0 @@
name: Run test case
on: [push, pull_request]
jobs:
run_test_case:
runs-on: ubuntu-latest
container:
image: erlang:22.3
steps:
- uses: actions/checkout@v1
- uses: actions/setup-java@v1
with:
java-version: '8.0.x'
java-package: jdk
- name: Code dialyzer
run: |
make xref
make dialyzer
- name: Run tests
run: |
make eunit
make ct
make cover
#- name: Coveralls
# env:
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# run: |
# make coveralls
- uses: actions/upload-artifact@v1
if: always()
with:
name: logs
path: _build/test/logs

View File

@ -1,29 +0,0 @@
name: Run test cases
on: [push, pull_request]
jobs:
run_test_cases:
runs-on: ubuntu-latest
container:
image: erlang:22.1
steps:
- uses: actions/checkout@v1
- name: run test cases
run: |
make xref
make eunit
make ct
make cover
- uses: actions/upload-artifact@v1
if: always()
with:
name: logs
path: _build/test/logs
- uses: actions/upload-artifact@v1
with:
name: cover
path: _build/test/cover

View File

@ -1,24 +0,0 @@
%%-*- mode: erlang -*-
%% .app.src.script
RemoveLeadingV =
fun(Tag) ->
case re:run(Tag, "^[v|e]?[0-9]\.[0-9]\.([0-9]|(rc|beta|alpha)\.[0-9])", [{capture, none}]) of
nomatch ->
re:replace(Tag, "/", "-", [{return ,list}]);
_ ->
%% if it is a version number prefixed by 'v' or 'e', then remove it
re:replace(Tag, "[v|e]", "", [{return ,list}])
end
end,
case os:getenv("EMQX_DEPS_DEFAULT_VSN") of
false -> CONFIG; % env var not defined
[] -> CONFIG; % env var set to empty string
Tag ->
[begin
AppConf0 = lists:keystore(vsn, 1, AppConf, {vsn, RemoveLeadingV(Tag)}),
{application, App, AppConf0}
end || Conf = {application, App, AppConf} <- CONFIG]
end.

View File

@ -1,29 +0,0 @@
name: Run test cases
on: [push, pull_request]
jobs:
run_test_cases:
runs-on: ubuntu-latest
container:
image: erlang:22.1
steps:
- uses: actions/checkout@v1
- name: run test cases
run: |
make xref
make eunit
make ct
make cover
- uses: actions/upload-artifact@v1
if: always()
with:
name: logs
path: _build/test/logs
- uses: actions/upload-artifact@v1
with:
name: cover
path: _build/test/cover

View File

@ -1,24 +0,0 @@
%%-*- mode: erlang -*-
%% .app.src.script
RemoveLeadingV =
fun(Tag) ->
case re:run(Tag, "^[v|e]?[0-9]\.[0-9]\.([0-9]|(rc|beta|alpha)\.[0-9])", [{capture, none}]) of
nomatch ->
re:replace(Tag, "/", "-", [{return ,list}]);
_ ->
%% if it is a version number prefixed by 'v' or 'e', then remove it
re:replace(Tag, "[v|e]", "", [{return ,list}])
end
end,
case os:getenv("EMQX_DEPS_DEFAULT_VSN") of
false -> CONFIG; % env var not defined
[] -> CONFIG; % env var set to empty string
Tag ->
[begin
AppConf0 = lists:keystore(vsn, 1, AppConf, {vsn, RemoveLeadingV(Tag)}),
{application, App, AppConf0}
end || Conf = {application, App, AppConf} <- CONFIG]
end.

View File

@ -1,30 +0,0 @@
name: Run test cases
on: [push, pull_request]
jobs:
run_test_cases:
runs-on: ubuntu-latest
container:
image: erlang:22.3
steps:
- uses: actions/checkout@v1
- name: run test cases
run: |
echo "https://zhanghongtong%40foxmail.com:${{ secrets.AccessToken }}@github.com" > $HOME/.git-credentials
git config --global credential.helper store
make eunit
make ct
make cover
- uses: actions/upload-artifact@v1
if: always()
with:
name: logs
path: _build/test/logs
- uses: actions/upload-artifact@v1
with:
name: cover
path: _build/test/cover

View File

@ -1,24 +0,0 @@
%%-*- mode: erlang -*-
%% .app.src.script
RemoveLeadingV =
fun(Tag) ->
case re:run(Tag, "^[v|e]?[0-9]\.[0-9]\.([0-9]|(rc|beta|alpha)\.[0-9])", [{capture, none}]) of
nomatch ->
re:replace(Tag, "/", "-", [{return ,list}]);
_ ->
%% if it is a version number prefixed by 'v' or 'e', then remove it
re:replace(Tag, "[v|e]", "", [{return ,list}])
end
end,
case os:getenv("EMQX_DEPS_DEFAULT_VSN") of
false -> CONFIG; % env var not defined
[] -> CONFIG; % env var set to empty string
Tag ->
[begin
AppConf0 = lists:keystore(vsn, 1, AppConf, {vsn, RemoveLeadingV(Tag)}),
{application, App, AppConf0}
end || Conf = {application, App, AppConf} <- CONFIG]
end.

View File

@ -1,30 +0,0 @@
{"4.2.3",
[
{"4.2.2", [
{load_module, emqx_mgmt, brutal_purge, soft_purge, []},
{load_module, emqx_mgmt_api_data, brutal_purge, soft_purge, []}
]},
{"4.2.1", [
{load_module, emqx_mgmt, brutal_purge, soft_purge, []},
{load_module, emqx_mgmt_api_data, brutal_purge, soft_purge, []}
]},
{"4.2.0", [
{load_module, emqx_mgmt, brutal_purge, soft_purge, []},
{load_module, emqx_mgmt_api_data, brutal_purge, soft_purge, []}
]}
],
[
{"4.2.2", [
{load_module, emqx_mgmt, brutal_purge, soft_purge, []},
{load_module, emqx_mgmt_api_data, brutal_purge, soft_purge, []}
]},
{"4.2.1", [
{load_module, emqx_mgmt, brutal_purge, soft_purge, []},
{load_module, emqx_mgmt_api_data, brutal_purge, soft_purge, []}
]},
{"4.2.0", [
{load_module, emqx_mgmt, brutal_purge, soft_purge, []},
{load_module, emqx_mgmt_api_data, brutal_purge, soft_purge, []}
]}
]
}.

View File

@ -1,88 +0,0 @@
name: Run test cases
on: [push, pull_request]
jobs:
#windows:
# runs-on: windows-latest
# steps:
# - uses: actions/checkout@v1
# - uses: ilammy/msvc-dev-cmd@v1
# - name: Run tests
# run: |
# set-executionpolicy remotesigned -s cu
# iex (new-object net.webclient).downloadstring('https://get.scoop.sh')
# scoop bucket add extras https://github.com/lukesampson/scoop-extras.git
# $env:path + ";" + $env:USERPROFILE + "\scoop\shims"
# scoop update
# scoop install sudo curl 7zip ojdkbuild8 vcredist2013
# scoop install erlang@22.3
# $rebar3 = $env:USERPROFILE + "\rebar3"
# (New-Object System.Net.WebClient).DownloadFile('https://s3.amazonaws.com/rebar3/rebar3', $rebar3)
# ## Code dialyzer
# escript $rebar3 xref
# escript $rebar3 dialyzer
# ## Run tests
# escript $rebar3 ct
mac:
runs-on: macos-latest
steps:
- uses: actions/checkout@v1
- name: Install compile env
run: |
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
brew install curl zip unzip gnu-sed erlang@22 openssl@1.1
echo "::add-path::/usr/local/opt/erlang@22/bin"
echo "::add-path::/usr/local/bin"
- name: install rebar3
run: |
curl -fsSL -o /usr/local/bin/rebar3 https://s3.amazonaws.com/rebar3/rebar3
chmod +x /usr/local/bin/rebar3
- name: Run tests
run: |
rebar3 ct
linux:
runs-on: ubuntu-latest
strategy:
matrix:
os:
- ubuntu18.04
- ubuntu16.04
- ubuntu14.04
- debian10
- debian9
- debian8
- opensuse
- centos7
- centos6
- raspbian10
- raspbian9
- raspbian8
steps:
- uses: actions/checkout@v1
- name: run test cases
env:
ERL_OTP: erl22.1
SYSTEM: ${{ matrix.os }}
run: |
version=$(echo ${{ github.ref }} | sed -r "s .*/.*/(.*) \1 g")
sudo docker run --rm --privileged multiarch/qemu-user-static:register --reset
sudo docker run -i --name $SYSTEM -v $(pwd):/emqx_passwd emqx/build-env:$ERL_OTP-$SYSTEM sh -c "cd /emqx_passwd && rebar3 ct"
docker:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: run test cases
env:
ERL_OTP: erl22.1
run: docker run -i --name alpine -v $(pwd):/emqx_passwd emqx/build-env:$ERL_OTP-alpine3.10-amd64 sh -c "cd /emqx_passwd && rebar3 ct"

View File

@ -1,14 +0,0 @@
%%-*- mode: erlang -*-
%% .app.src.script
Config = case os:type() =:= {win32, nt} orelse os:getenv("EMQX_DESC") =:= "EMQ X Edge" of
true ->
[begin
Applications0 = proplists:get_value(applications, AppConf),
Applications = Applications0 -- [bcrypt],
AppConf0 = lists:keystore(applications, 1, AppConf, {applications, Applications}),
{application, App, AppConf0}
end || Conf = {application, App, AppConf} <- CONFIG];
false ->
CONFIG
end.

View File

@ -1,28 +0,0 @@
name: Run test cases
on: [push, pull_request]
jobs:
run_test_cases:
runs-on: ubuntu-latest
container:
image: erlang:22.1
steps:
- uses: actions/checkout@v1
- name: run test cases
run: |
make eunit
make ct
make cover
- uses: actions/upload-artifact@v1
if: always()
with:
name: logs
path: _build/test/logs
- uses: actions/upload-artifact@v1
with:
name: cover
path: _build/test/cover

View File

@ -1,24 +0,0 @@
%%-*- mode: erlang -*-
%% .app.src.script
RemoveLeadingV =
fun(Tag) ->
case re:run(Tag, "^[v|e]?[0-9]\.[0-9]\.([0-9]|(rc|beta|alpha)\.[0-9])", [{capture, none}]) of
nomatch ->
re:replace(Tag, "/", "-", [{return ,list}]);
_ ->
%% if it is a version number prefixed by 'v' or 'e', then remove it
re:replace(Tag, "[v|e]", "", [{return ,list}])
end
end,
case os:getenv("EMQX_DEPS_DEFAULT_VSN") of
false -> CONFIG; % env var not defined
[] -> CONFIG; % env var set to empty string
Tag ->
[begin
AppConf0 = lists:keystore(vsn, 1, AppConf, {vsn, RemoveLeadingV(Tag)}),
{application, App, AppConf0}
end || Conf = {application, App, AppConf} <- CONFIG]
end.

View File

@ -1,29 +0,0 @@
name: Run test cases
on: [push, pull_request]
jobs:
run_test_cases:
runs-on: ubuntu-latest
container:
image: erlang:22.1
steps:
- uses: actions/checkout@v1
- name: run test cases
run: |
make xref
make eunit
make ct
make cover
- uses: actions/upload-artifact@v1
if: always()
with:
name: logs
path: _build/test/logs
- uses: actions/upload-artifact@v1
with:
name: cover
path: _build/test/cover

View File

@ -1,24 +0,0 @@
%%-*- mode: erlang -*-
%% .app.src.script
RemoveLeadingV =
fun(Tag) ->
case re:run(Tag, "^[v|e]?[0-9]\.[0-9]\.([0-9]|(rc|beta|alpha)\.[0-9])", [{capture, none}]) of
nomatch ->
re:replace(Tag, "/", "-", [{return ,list}]);
_ ->
%% if it is a version number prefixed by 'v' or 'e', then remove it
re:replace(Tag, "[v|e]", "", [{return ,list}])
end
end,
case os:getenv("EMQX_DEPS_DEFAULT_VSN") of
false -> CONFIG; % env var not defined
[] -> CONFIG; % env var set to empty string
Tag ->
[begin
AppConf0 = lists:keystore(vsn, 1, AppConf, {vsn, RemoveLeadingV(Tag)}),
{application, App, AppConf0}
end || Conf = {application, App, AppConf} <- CONFIG]
end.

View File

@ -1,29 +0,0 @@
name: Run test cases
on: [push, pull_request]
jobs:
run_test_cases:
runs-on: ubuntu-latest
container:
image: erlang:22.1
steps:
- uses: actions/checkout@v1
- name: run test cases
run: |
make xref
make eunit
make ct
make cover
- uses: actions/upload-artifact@v1
if: always()
with:
name: logs
path: _build/test/logs
- uses: actions/upload-artifact@v1
with:
name: cover
path: _build/test/cover

View File

@ -1,24 +0,0 @@
%%-*- mode: erlang -*-
%% .app.src.script
RemoveLeadingV =
fun(Tag) ->
case re:run(Tag, "^[v|e]?[0-9]\.[0-9]\.([0-9]|(rc|beta|alpha)\.[0-9])", [{capture, none}]) of
nomatch ->
re:replace(Tag, "/", "-", [{return ,list}]);
_ ->
%% if it is a version number prefixed by 'v' or 'e', then remove it
re:replace(Tag, "[v|e]", "", [{return ,list}])
end
end,
case os:getenv("EMQX_DEPS_DEFAULT_VSN") of
false -> CONFIG; % env var not defined
[] -> CONFIG; % env var set to empty string
Tag ->
[begin
AppConf0 = lists:keystore(vsn, 1, AppConf, {vsn, RemoveLeadingV(Tag)}),
{application, App, AppConf0}
end || Conf = {application, App, AppConf} <- CONFIG]
end.

View File

@ -1,29 +0,0 @@
name: Run test cases
on: [push, pull_request]
jobs:
run_test_cases:
runs-on: ubuntu-latest
container:
image: erlang:22.1
steps:
- uses: actions/checkout@v1
- name: run test cases
run: |
make xref
make eunit
make ct
make cover
- uses: actions/upload-artifact@v1
if: always()
with:
name: logs
path: _build/test/logs
- uses: actions/upload-artifact@v1
with:
name: cover
path: _build/test/cover

View File

@ -1,24 +0,0 @@
%%-*- mode: erlang -*-
%% .app.src.script
RemoveLeadingV =
fun(Tag) ->
case re:run(Tag, "^[v|e]?[0-9]\.[0-9]\.([0-9]|(rc|beta|alpha)\.[0-9])", [{capture, none}]) of
nomatch ->
re:replace(Tag, "/", "-", [{return ,list}]);
_ ->
%% if it is a version number prefixed by 'v' or 'e', then remove it
re:replace(Tag, "[v|e]", "", [{return ,list}])
end
end,
case os:getenv("EMQX_DEPS_DEFAULT_VSN") of
false -> CONFIG; % env var not defined
[] -> CONFIG; % env var set to empty string
Tag ->
[begin
AppConf0 = lists:keystore(vsn, 1, AppConf, {vsn, RemoveLeadingV(Tag)}),
{application, App, AppConf0}
end || Conf = {application, App, AppConf} <- CONFIG]
end.

View File

@ -1,29 +0,0 @@
name: Run test cases
on: [push, pull_request]
jobs:
run_test_cases:
runs-on: ubuntu-latest
container:
image: erlang:22.1
steps:
- uses: actions/checkout@v1
- name: run test cases
run: |
make xref
make eunit
make ct
make cover
- uses: actions/upload-artifact@v1
if: always()
with:
name: logs
path: _build/test/logs
- uses: actions/upload-artifact@v1
with:
name: cover
path: _build/test/cover

View File

@ -1,24 +0,0 @@
%%-*- mode: erlang -*-
%% .app.src.script
RemoveLeadingV =
fun(Tag) ->
case re:run(Tag, "^[v|e]?[0-9]\.[0-9]\.([0-9]|(rc|beta|alpha)\.[0-9])", [{capture, none}]) of
nomatch ->
re:replace(Tag, "/", "-", [{return ,list}]);
_ ->
%% if it is a version number prefixed by 'v' or 'e', then remove it
re:replace(Tag, "[v|e]", "", [{return ,list}])
end
end,
case os:getenv("EMQX_DEPS_DEFAULT_VSN") of
false -> CONFIG; % env var not defined
[] -> CONFIG; % env var set to empty string
Tag ->
[begin
AppConf0 = lists:keystore(vsn, 1, AppConf, {vsn, RemoveLeadingV(Tag)}),
{application, App, AppConf0}
end || Conf = {application, App, AppConf} <- CONFIG]
end.

View File

@ -1,29 +0,0 @@
name: Run test cases
on: [push, pull_request]
jobs:
run_test_cases:
runs-on: ubuntu-latest
container:
image: erlang:22.1
steps:
- uses: actions/checkout@v1
- name: run test cases
run: |
#make xref
make eunit
make ct
make cover
- uses: actions/upload-artifact@v1
if: always()
with:
name: logs
path: _build/test/logs
- uses: actions/upload-artifact@v1
with:
name: cover
path: _build/test/cover

View File

@ -1,24 +0,0 @@
%%-*- mode: erlang -*-
%% .app.src.script
RemoveLeadingV =
fun(Tag) ->
case re:run(Tag, "^[v|e]?[0-9]\.[0-9]\.([0-9]|(rc|beta|alpha)\.[0-9])", [{capture, none}]) of
nomatch ->
re:replace(Tag, "/", "-", [{return ,list}]);
_ ->
%% if it is a version number prefixed by 'v' or 'e', then remove it
re:replace(Tag, "[v|e]", "", [{return ,list}])
end
end,
case os:getenv("EMQX_DEPS_DEFAULT_VSN") of
false -> CONFIG; % env var not defined
[] -> CONFIG; % env var set to empty string
Tag ->
[begin
AppConf0 = lists:keystore(vsn, 1, AppConf, {vsn, RemoveLeadingV(Tag)}),
{application, App, AppConf0}
end || Conf = {application, App, AppConf} <- CONFIG]
end.

View File

@ -1,44 +0,0 @@
%% -*-: erlang -*-
{"4.2.3",
[
{"4.2.0", [
{load_module, emqx_rule_events, brutal_purge, soft_purge, []},
{load_module, emqx_rule_funcs, brutal_purge, soft_purge, []},
{load_module, emqx_rule_maps, brutal_purge, soft_purge, []},
{load_module, emqx_rule_engine, brutal_purge, soft_purge, []},
{load_module, emqx_rule_actions, brutal_purge, soft_purge, []}
]},
{"4.2.1", [
{load_module, emqx_rule_funcs, brutal_purge, soft_purge, []},
{load_module, emqx_rule_maps, brutal_purge, soft_purge, []},
{load_module, emqx_rule_engine, brutal_purge, soft_purge, []},
{load_module, emqx_rule_actions, brutal_purge, soft_purge, []}
]},
{"4.2.2", [
{load_module, emqx_rule_funcs, brutal_purge, soft_purge, []},
{load_module, emqx_rule_engine, brutal_purge, soft_purge, []},
{load_module, emqx_rule_actions, brutal_purge, soft_purge, []}
]}
],
[
{"4.2.0", [
{load_module, emqx_rule_events, brutal_purge, soft_purge, []},
{load_module, emqx_rule_funcs, brutal_purge, soft_purge, []},
{load_module, emqx_rule_maps, brutal_purge, soft_purge, []},
{load_module, emqx_rule_engine, brutal_purge, soft_purge, []},
{load_module, emqx_rule_actions, brutal_purge, soft_purge, []}
]},
{"4.2.1", [
{load_module, emqx_rule_funcs, brutal_purge, soft_purge, []},
{load_module, emqx_rule_maps, brutal_purge, soft_purge, []},
{load_module, emqx_rule_engine, brutal_purge, soft_purge, []},
{load_module, emqx_rule_actions, brutal_purge, soft_purge, []}
]},
{"4.2.2", [
{load_module, emqx_rule_actions, brutal_purge, soft_purge, []},
{load_module, emqx_rule_engine, brutal_purge, soft_purge, []},
{load_module, emqx_rule_funcs, brutal_purge, soft_purge, []}
]}
]
}.

View File

@ -1,31 +0,0 @@
name: Run test case
on:
push:
pull_request:
jobs:
run_test_case:
runs-on: ubuntu-latest
container:
image: erlang:22.1
steps:
- uses: actions/checkout@v1
- name: Run tests
run: |
make eunit
make ct
make cover
- uses: actions/upload-artifact@v1
if: always()
with:
name: logs
path: _build/test/logs
- uses: actions/upload-artifact@v1
with:
name: cover
path: _build/test/cover

View File

@ -1,33 +0,0 @@
%%-*- mode: erlang -*-
%% .app.src.script
Config = case os:getenv("EMQX_DESC") of
false -> CONFIG; % env var not defined
[] -> CONFIG; % env var set to empty string
Desc ->
[begin
AppConf0 = lists:keystore(description, 1, AppConf, {description, Desc}),
{application, App, AppConf0}
end || Conf = {application, App, AppConf} <- CONFIG]
end,
RemoveLeadingV =
fun(Tag) ->
case re:run(Tag, "^[v|e]?[0-9]\.[0-9]\.([0-9]|(rc|beta|alpha)\.[0-9])", [{capture, none}]) of
nomatch ->
re:replace(Tag, "/", "-", [{return ,list}]);
_ ->
%% if it is a version number prefixed by 'v' or 'e', then remove it
re:replace(Tag, "[v|e]", "", [{return ,list}])
end
end,
case os:getenv("EMQX_DEPS_DEFAULT_VSN") of
false -> Config; % env var not defined
[] -> Config; % env var set to empty string
Tag ->
[begin
AppConf0 = lists:keystore(vsn, 1, AppConf, {vsn, RemoveLeadingV(Tag)}),
{application, App, AppConf0}
end || Conf = {application, App, AppConf} <- Config]
end.

View File

@ -1,30 +0,0 @@
{"4.2.3",
[
{"4.2.0", [
{load_module, emqx_sasl_api, brutal_purge, soft_purge, []},
{load_module, emqx_sasl_cli, brutal_purge, soft_purge, []}
]},
{"4.2.1", [
{load_module, emqx_sasl_api, brutal_purge, soft_purge, []},
{load_module, emqx_sasl_cli, brutal_purge, soft_purge, []}
]},
{"4.2.2", [
{load_module, emqx_sasl_api, brutal_purge, soft_purge, []},
{load_module, emqx_sasl_cli, brutal_purge, soft_purge, []}
]}
],
[
{"4.2.0", [
{load_module, emqx_sasl_api, brutal_purge, soft_purge, []},
{load_module, emqx_sasl_cli, brutal_purge, soft_purge, []}
]},
{"4.2.1", [
{load_module, emqx_sasl_api, brutal_purge, soft_purge, []},
{load_module, emqx_sasl_cli, brutal_purge, soft_purge, []}
]},
{"4.2.2", [
{load_module, emqx_sasl_api, brutal_purge, soft_purge, []},
{load_module, emqx_sasl_cli, brutal_purge, soft_purge, []}
]}
]
}.

View File

@ -1,31 +0,0 @@
name: Run test cases
on: [push, pull_request]
jobs:
run_test_cases:
runs-on: ubuntu-latest
container:
image: erlang:22.1
steps:
- uses: actions/checkout@v1
- name: run test cases
run: |
make dialyzer
make xref
make eunit
make ct
make proper
make cover
- uses: actions/upload-artifact@v1
if: always()
with:
name: logs
path: _build/test/logs
- uses: actions/upload-artifact@v1
with:
name: cover
path: _build/test/cover

View File

@ -1,24 +0,0 @@
%%-*- mode: erlang -*-
%% .app.src.script
RemoveLeadingV =
fun(Tag) ->
case re:run(Tag, "^[v|e]?[0-9]\.[0-9]\.([0-9]|(rc|beta|alpha)\.[0-9])", [{capture, none}]) of
nomatch ->
re:replace(Tag, "/", "-", [{return ,list}]);
_ ->
%% if it is a version number prefixed by 'v' or 'e', then remove it
re:replace(Tag, "[v|e]", "", [{return ,list}])
end
end,
case os:getenv("EMQX_DEPS_DEFAULT_VSN") of
false -> CONFIG; % env var not defined
[] -> CONFIG; % env var set to empty string
Tag ->
[begin
AppConf0 = lists:keystore(vsn, 1, AppConf, {vsn, RemoveLeadingV(Tag)}),
{application, App, AppConf0}
end || Conf = {application, App, AppConf} <- CONFIG]
end.

View File

@ -1,25 +0,0 @@
%% -*-: erlang -*-
{"4.2.3",
[
{"4.2.2", [
{load_module, emqx_sn_gateway, brutal_purge, soft_purge, []}
]},
{"4.2.1", [
{load_module, emqx_sn_gateway, brutal_purge, soft_purge, []}
]},
{"4.2.0", [
{load_module, emqx_sn_gateway, brutal_purge, soft_purge, []}
]}
],
[
{"4.2.2", [
{load_module, emqx_sn_gateway, brutal_purge, soft_purge, []}
]},
{"4.2.1", [
{load_module, emqx_sn_gateway, brutal_purge, soft_purge, []}
]},
{"4.2.0", [
{load_module, emqx_sn_gateway, brutal_purge, soft_purge, []}
]}
]
}.

View File

@ -1,29 +0,0 @@
name: Run test cases
on: [push, pull_request]
jobs:
run_test_cases:
runs-on: ubuntu-latest
container:
image: erlang:22.1
steps:
- uses: actions/checkout@v1
- name: run test cases
run: |
make xref
make eunit
make ct
make cover
- uses: actions/upload-artifact@v1
if: always()
with:
name: logs
path: _build/test/logs
- uses: actions/upload-artifact@v1
with:
name: cover
path: _build/test/cover

View File

@ -1,24 +0,0 @@
%%-*- mode: erlang -*-
%% .app.src.script
RemoveLeadingV =
fun(Tag) ->
case re:run(Tag, "^[v|e]?[0-9]\.[0-9]\.([0-9]|(rc|beta|alpha)\.[0-9])", [{capture, none}]) of
nomatch ->
re:replace(Tag, "/", "-", [{return ,list}]);
_ ->
%% if it is a version number prefixed by 'v' or 'e', then remove it
re:replace(Tag, "[v|e]", "", [{return ,list}])
end
end,
case os:getenv("EMQX_DEPS_DEFAULT_VSN") of
false -> CONFIG; % env var not defined
[] -> CONFIG; % env var set to empty string
Tag ->
[begin
AppConf0 = lists:keystore(vsn, 1, AppConf, {vsn, RemoveLeadingV(Tag)}),
{application, App, AppConf0}
end || Conf = {application, App, AppConf} <- CONFIG]
end.

View File

@ -1,24 +0,0 @@
{"4.2.3",
[
{"4.2.2", [
{load_module, emqx_telemetry, brutal_purge, soft_purge, []}
]},
{"4.2.1", [
{load_module, emqx_telemetry, brutal_purge, soft_purge, []}
]},
{"4.2.0", [
{load_module, emqx_telemetry, brutal_purge, soft_purge, []}
]}
],
[
{"4.2.2", [
{load_module, emqx_telemetry, brutal_purge, soft_purge, []}
]},
{"4.2.1", [
{load_module, emqx_telemetry, brutal_purge, soft_purge, []}
]},
{"4.2.0", [
{load_module, emqx_telemetry, brutal_purge, soft_purge, []}
]}
]
}.

View File

@ -1,33 +0,0 @@
name: Run test cases
on: [push, pull_request]
jobs:
run_test_cases:
runs-on: ubuntu-latest
container:
image: erlang:22.1
steps:
- uses: actions/checkout@v1
- name: code dialyzer
run: |
make xref
make dialyzer
- name: run test cases
run: |
make eunit
make ct
make proper
make cover
- uses: actions/upload-artifact@v1
if: always()
with:
name: logs
path: _build/test/logs
- uses: actions/upload-artifact@v1
with:
name: cover
path: _build/test/cover

View File

@ -1,24 +0,0 @@
%%-*- mode: erlang -*-
%% .app.src.script
RemoveLeadingV =
fun(Tag) ->
case re:run(Tag, "^[v|e]?[0-9]\.[0-9]\.([0-9]|(rc|beta|alpha)\.[0-9])", [{capture, none}]) of
nomatch ->
re:replace(Tag, "/", "-", [{return ,list}]);
_ ->
%% if it is a version number prefixed by 'v' or 'e', then remove it
re:replace(Tag, "[v|e]", "", [{return ,list}])
end
end,
case os:getenv("EMQX_DEPS_DEFAULT_VSN") of
false -> CONFIG; % env var not defined
[] -> CONFIG; % env var set to empty string
Tag ->
[begin
AppConf0 = lists:keystore(vsn, 1, AppConf, {vsn, RemoveLeadingV(Tag)}),
{application, App, AppConf0}
end || Conf = {application, App, AppConf} <- CONFIG]
end.

View File

@ -1,36 +0,0 @@
%% -*-: erlang -*-
{"4.2.3",
[
{"4.2.2", [
{load_module, emqx_web_hook_actions, brutal_purge, soft_purge, []},
{apply, {emqx_rule_engine, load_providers, []}}
]},
{"4.2.1", [
{load_module, emqx_web_hook, brutal_purge, soft_purge, []},
{load_module, emqx_web_hook_actions, brutal_purge, soft_purge, [emqx_rule_engine]},
{apply, {emqx_rule_engine, load_providers, []}}
]},
{"4.2.0", [
{load_module, emqx_web_hook, brutal_purge, soft_purge, []},
{load_module, emqx_web_hook_actions, brutal_purge, soft_purge, [emqx_rule_engine]},
{apply, {emqx_rule_engine, load_providers, []}}
]}
],
[
{"4.2.2", [
{load_module, emqx_web_hook_actions, brutal_purge, soft_purge, []},
{apply, {emqx_rule_engine, load_providers, []}}
]},
{"4.2.1", [
{load_module, emqx_web_hook, brutal_purge, soft_purge, []},
{load_module, emqx_web_hook_actions, brutal_purge, soft_purge, [emqx_rule_engine]},
{apply, {emqx_rule_engine, load_providers, []}}
]},
{"4.2.0", [
{load_module, emqx_web_hook, brutal_purge, soft_purge, []},
{load_module, emqx_web_hook_actions, brutal_purge, soft_purge, [emqx_rule_engine]},
{apply, {emqx_rule_engine, load_providers, []}}
]}
]
}.

View File

@ -8,7 +8,6 @@ apps=(
"emqx_auth_http"
"emqx_auth_jwt"
"emqx_auth_ldap"
"emqx_auth_mnesia"
"emqx_auth_mongo"
"emqx_auth_mysql"
"emqx_auth_pgsql"
@ -57,6 +56,7 @@ download_zip() {
default_vsn="dev/v4.3.0"
download_zip "emqx_passwd" "v1.1.1"
download_zip "emqx_auth_mnesia" "e4.2.2"
for app in ${apps[@]}; do
download_zip "$app" "$default_vsn"
done
@ -79,17 +79,22 @@ extract_zip(){
}
extract_zip "emqx_passwd" "v1.1.1" "1.1.1"
extract_zip "emqx_auth_mnesia" "e4.2.2" "e4.2.2"
for app in ${apps[@]}; do
extract_zip "$app" "$default_vsn"
done
cleanup_app(){
local app="$1"
rm -f "apps/$app/Makefile"
rm -f "apps/$app/rebar.config.script"
pushd "apps/$app"
rm -f Makefile rebar.config.script
rm -rf ".github" ".ci"
rm -rf src/*.app.src.script
rm -rf src/*.appup.src
popd
}
apps+=( "emqx_passwd" )
apps+=( "emqx_passwd" "emqx_auth_mnesia" )
for app in ${apps[@]}; do
cleanup_app $app
done