Zaiming Shi 5 rokov pred
rodič
commit
8a12018863
86 zmenil súbory, kde vykonal 1015 pridanie a 2740 odobranie
  1. 0 0
      apps/.gitkeep
  2. 0 26
      apps/emqx_auth_http/.github/workflows/run_test_cases.yaml
  3. 0 24
      apps/emqx_auth_http/src/emqx_auth_http.app.src.script
  4. 0 29
      apps/emqx_auth_jwt/.github/workflows/run_test_cases.yaml
  5. 0 24
      apps/emqx_auth_jwt/src/emqx_auth_jwt.app.src.script
  6. 0 26
      apps/emqx_auth_ldap/.ci/docker-compose.yml
  7. 0 26
      apps/emqx_auth_ldap/.ci/emqx-ldap/Dockerfile
  8. 0 16
      apps/emqx_auth_ldap/.ci/emqx-ldap/slapd.conf
  9. 0 49
      apps/emqx_auth_ldap/.github/workflows/run_test_cases.yaml
  10. 0 24
      apps/emqx_auth_ldap/src/emqx_auth_ldap.app.src.script
  11. 0 29
      apps/emqx_auth_mnesia/.github/workflows/run_test_cases.yaml
  12. 26 16
      apps/emqx_auth_mnesia/etc/emqx_auth_mnesia.conf
  13. 13 10
      apps/emqx_auth_mnesia/include/emqx_auth_mnesia.hrl
  14. 23 15
      apps/emqx_auth_mnesia/priv/emqx_auth_mnesia.schema
  15. 1 1
      apps/emqx_auth_mnesia/rebar.config
  16. 53 33
      apps/emqx_auth_mnesia/src/emqx_acl_mnesia.erl
  17. 137 48
      apps/emqx_auth_mnesia/src/emqx_acl_mnesia_api.erl
  18. 198 0
      apps/emqx_auth_mnesia/src/emqx_acl_mnesia_cli.erl
  19. 0 24
      apps/emqx_auth_mnesia/src/emqx_auth_mnesia.app.src.script
  20. 32 24
      apps/emqx_auth_mnesia/src/emqx_auth_mnesia.erl
  21. 200 82
      apps/emqx_auth_mnesia/src/emqx_auth_mnesia_api.erl
  22. 13 13
      apps/emqx_auth_mnesia/src/emqx_auth_mnesia_app.erl
  23. 79 91
      apps/emqx_auth_mnesia/src/emqx_auth_mnesia_cli.erl
  24. 97 161
      apps/emqx_auth_mnesia/test/emqx_acl_mnesia_SUITE.erl
  25. 134 109
      apps/emqx_auth_mnesia/test/emqx_auth_mnesia_SUITE.erl
  26. 0 59
      apps/emqx_auth_mongo/.github/workflows/run_test_cases.yaml
  27. 0 24
      apps/emqx_auth_mongo/src/emqx_auth_mongo.app.src.script
  28. 0 37
      apps/emqx_auth_mongo/src/emqx_auth_mongo.appup.src
  29. 0 59
      apps/emqx_auth_mysql/.github/workflows/run_test_cases.yaml
  30. 0 24
      apps/emqx_auth_mysql/src/emqx_auth_mysql.app.src.script
  31. 0 25
      apps/emqx_auth_mysql/src/emqx_auth_mysql.appup.src
  32. 0 30
      apps/emqx_auth_pgsql/.ci/docker-compose.yml
  33. 0 8
      apps/emqx_auth_pgsql/.ci/pgsql/Dockerfile
  34. 0 51
      apps/emqx_auth_pgsql/.github/workflows/run_test_cases.yaml
  35. 0 24
      apps/emqx_auth_pgsql/src/emqx_auth_pgsql.app.src.script
  36. 0 25
      apps/emqx_auth_pgsql/src/emqx_auth_pgsql.appup.src
  37. 0 31
      apps/emqx_auth_redis/.ci/docker-compose-tls.yml
  38. 0 25
      apps/emqx_auth_redis/.ci/docker-compose.yml
  39. 0 80
      apps/emqx_auth_redis/.github/workflows/run_test_cases.yaml
  40. 0 24
      apps/emqx_auth_redis/src/emqx_auth_redis.app.src.script
  41. 0 36
      apps/emqx_auth_redis/src/emqx_auth_redis.appup.src
  42. 0 28
      apps/emqx_bridge_mqtt/.github/workflows/run_test_cases.yaml
  43. 0 24
      apps/emqx_bridge_mqtt/src/emqx_bridge_mqtt.app.src.script
  44. 0 32
      apps/emqx_bridge_mqtt/src/emqx_bridge_mqtt.appup.src
  45. 0 29
      apps/emqx_coap/.github/workflows/run_test_cases.yaml
  46. 0 24
      apps/emqx_coap/src/emqx_coap.app.src.script
  47. 0 29
      apps/emqx_dashboard/.github/workflows/run_test_cases.yaml
  48. 0 24
      apps/emqx_dashboard/src/emqx_dashboard.app.src.script
  49. 0 18
      apps/emqx_dashboard/src/emqx_dashboard.appup.src
  50. 0 31
      apps/emqx_exhook/.github/workflows/run_test_cases.yaml
  51. 0 38
      apps/emqx_exproto/.github/workflows/run_test_case.yaml
  52. 0 29
      apps/emqx_lua_hook/.github/workflows/run_test_cases.yaml
  53. 0 24
      apps/emqx_lua_hook/src/emqx_lua_hook.app.src.script
  54. 0 29
      apps/emqx_lwm2m/.github/workflows/run_test_cases.yaml
  55. 0 24
      apps/emqx_lwm2m/src/emqx_lwm2m.app.src.script
  56. 0 30
      apps/emqx_management/.github/workflows/run_test_cases.yaml
  57. 0 24
      apps/emqx_management/src/emqx_management.app.src.script
  58. 0 30
      apps/emqx_management/src/emqx_management.appup.src
  59. 0 88
      apps/emqx_passwd/.github/workflows/run_test_cases.yaml
  60. 0 14
      apps/emqx_passwd/src/emqx_passwd.app.src.script
  61. 0 28
      apps/emqx_plugin_template/.github/workflows/run_test_cases.yaml
  62. 0 24
      apps/emqx_plugin_template/src/emqx_plugin_template.app.src.script
  63. 0 29
      apps/emqx_prometheus/.github/workflows/run_test_cases.yaml
  64. 0 24
      apps/emqx_prometheus/src/emqx_prometheus.app.src.script
  65. 0 29
      apps/emqx_psk_file/.github/workflows/run_test_cases.yaml
  66. 0 24
      apps/emqx_psk_file/src/emqx_psk_file.app.src.script
  67. 0 29
      apps/emqx_recon/.github/workflows/run_test_cases.yaml
  68. 0 24
      apps/emqx_recon/src/emqx_recon.app.src.script
  69. 0 29
      apps/emqx_retainer/.github/workflows/run_test_cases.yaml
  70. 0 24
      apps/emqx_retainer/src/emqx_retainer.app.src.script
  71. 0 29
      apps/emqx_rule_engine/.github/workflows/run_test_cases.yaml
  72. 0 24
      apps/emqx_rule_engine/src/emqx_rule_engine.app.src.script
  73. 0 44
      apps/emqx_rule_engine/src/emqx_rule_engine.appup.src
  74. 0 31
      apps/emqx_sasl/.github/workflows/run_test_case.yaml
  75. 0 33
      apps/emqx_sasl/src/emqx_sasl.app.src.script
  76. 0 30
      apps/emqx_sasl/src/emqx_sasl.appup.src
  77. 0 31
      apps/emqx_sn/.github/workflows/run_test_cases.yaml
  78. 0 24
      apps/emqx_sn/src/emqx_sn.app.src.script
  79. 0 25
      apps/emqx_sn/src/emqx_sn.appup.src
  80. 0 29
      apps/emqx_stomp/.github/workflows/run_test_cases.yaml
  81. 0 24
      apps/emqx_stomp/src/emqx_stomp.app.src.script
  82. 0 24
      apps/emqx_telemetry/src/emqx_telemetry.appup.src
  83. 0 33
      apps/emqx_web_hook/.github/workflows/run_test_cases.yaml
  84. 0 24
      apps/emqx_web_hook/src/emqx_web_hook.app.src.script
  85. 0 36
      apps/emqx_web_hook/src/emqx_web_hook.appup.src
  86. 9 4
      sync-apps.sh

+ 0 - 0
apps/.gitkeep


+ 0 - 26
apps/emqx_auth_http/.github/workflows/run_test_cases.yaml

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

+ 0 - 24
apps/emqx_auth_http/src/emqx_auth_http.app.src.script

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

+ 0 - 29
apps/emqx_auth_jwt/.github/workflows/run_test_cases.yaml

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

+ 0 - 24
apps/emqx_auth_jwt/src/emqx_auth_jwt.app.src.script

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

+ 0 - 26
apps/emqx_auth_ldap/.ci/docker-compose.yml

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

+ 0 - 26
apps/emqx_auth_ldap/.ci/emqx-ldap/Dockerfile

@@ -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 []

+ 0 - 16
apps/emqx_auth_ldap/.ci/emqx-ldap/slapd.conf

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

+ 0 - 49
apps/emqx_auth_ldap/.github/workflows/run_test_cases.yaml

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

+ 0 - 24
apps/emqx_auth_ldap/src/emqx_auth_ldap.app.src.script

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

+ 0 - 29
apps/emqx_auth_mnesia/.github/workflows/run_test_cases.yaml

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

+ 26 - 16
apps/emqx_auth_mnesia/etc/emqx_auth_mnesia.conf

@@ -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~!@#$%^&*()_+

+ 13 - 10
apps/emqx_auth_mnesia/include/emqx_auth_mnesia.hrl

@@ -1,17 +1,20 @@
 -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, {
         success = 'client.auth.success',
@@ -32,4 +35,4 @@
 -define(AUTH_METRICS(K), ?METRICS(auth_metrics, K)).
 
 -define(ACL_METRICS, ?METRICS(acl_metrics)).
--define(ACL_METRICS(K), ?METRICS(acl_metrics, K)).
+-define(ACL_METRICS(K), ?METRICS(acl_metrics, K)).

+ 23 - 15
apps/emqx_auth_mnesia/priv/emqx_auth_mnesia.schema

@@ -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.clientid_list", fun(Conf) ->
+  ClientList = cuttlefish_variable:filter_by_prefix("auth.client", Conf),
+  lists:foldl(
+       fun({["auth", "client", Id, "clientid"], ClientId}, AccIn) ->
+        [{ClientId, cuttlefish:conf_get("auth.client." ++ Id ++ ".password", Conf)} | AccIn];
+       (_, AccIn) ->
+        AccIn
+       end, [], ClientList)
+end}.
+
+{mapping, "auth.user.$id.username", "emqx_auth_mnesia.username_list", [
+  {datatype, string}
+]}.
+
+{mapping, "auth.user.$id.password", "emqx_auth_mnesia.username_list", [
+  {datatype, string}
 ]}.
 
-{translation, "emqx_auth_mnesia.userlist", fun(Conf) ->
-  Userlist = cuttlefish_variable:filter_by_prefix("auth.mnesia", Conf),
+{translation, "emqx_auth_mnesia.username_list", fun(Conf) ->
+  Userlist = cuttlefish_variable:filter_by_prefix("auth.user", 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", "user", Id, "username"], Username}, AccIn) ->
+        [{Username, cuttlefish:conf_get("auth.user." ++ Id ++ ".password", Conf)} | AccIn];
        (_, AccIn) ->
         AccIn
        end, [], Userlist)

+ 1 - 1
apps/emqx_auth_mnesia/rebar.config

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

+ 53 - 33
apps/emqx_auth_mnesia/src/emqx_acl_mnesia.erl

@@ -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]).

+ 137 - 48
apps/emqx_auth_mnesia/src/emqx_acl_mnesia_api.erl

@@ -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   => "/acl/clientid",
+            func   => list_clientid,
+            descr  => "List available mnesia in the cluster"
+           }).
+
+-rest_api(#{name   => list_username,
             method => 'GET',
-            path   => "/mqtt_acl",
-            func   => list,
+            path   => "/acl/username",
+            func   => list_username,
             descr  => "List available mnesia in the cluster"
            }).
 
--rest_api(#{name   => lookup_emqx_acl,
+-rest_api(#{name   => list_all,
+            method => 'GET',
+            path   => "/acl/$all",
+            func   => list_all,
+            descr  => "List available mnesia in the cluster"
+           }).
+
+-rest_api(#{name   => lookup_clientid,
+            method => 'GET',
+            path   => "/acl/clientid/:bin:clientid",
+            func   => lookup,
+            descr  => "Lookup mnesia in the cluster"
+           }).
+
+-rest_api(#{name   => lookup_username,
             method => 'GET',
-            path   => "/mqtt_acl/:bin:login",
+            path   => "/acl/username/:bin:username",
             func   => lookup,
             descr  => "Lookup mnesia in the cluster"
            }).
 
--rest_api(#{name   => add_emqx_acl,
+-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   => "/acl/clientid/:bin:clientid/topic/:bin:topic",
+            func   => delete,
+            descr  => "Delete mnesia in the cluster"
+           }).
+
+-rest_api(#{name   => delete_username,
             method => 'DELETE',
-            path   => "/mqtt_acl/:bin:login/:bin:topic",
+            path   => "/acl/username/:bin:username/topic/:bin:topic",
             func   => delete,
             descr  => "Delete mnesia in the cluster"
            }).
 
--export([ list/2
+-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)}).
+
+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)}).
 
-lookup(#{login := Login}, _Params) ->
-    return({ok, format(emqx_auth_mnesia_cli:lookup_acl(urldecode(Login)))}).
+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)),
+do_add([ Params | ParamsN ], ReList) ->
+    do_add(ParamsN, [do_add(Params) | ReList]);
+
+do_add([], ReList) ->
+    {ok, ReList}.
+
+do_add(Params) ->
+    Clientid = get_value(<<"clientid">>, Params, undefined),
+    Username = get_value(<<"username">>, Params, undefined),
+    Login = case {Clientid, Username} of
+                {undefined, undefined} -> all;
+                {_, undefined} -> {clientid, urldecode(Clientid)};
+                {undefined, _} -> {username, urldecode(Username)}
+            end,
     Topic = urldecode(get_value(<<"topic">>, Params)),
     Action = urldecode(get_value(<<"action">>, Params)),
-    Allow = get_value(<<"allow">>, Params),
-    Re = case validate([login, topic, action, allow], [Login, Topic, Action, Allow]) of
+    Access = urldecode(get_value(<<"access">>, Params)),
+    Re = case validate([login, topic, action, access], [Login, Topic, Action, Access]) of
         ok -> 
-            emqx_auth_mnesia_cli:add_acl(Login, Topic, Action, Allow);
+            emqx_acl_mnesia_cli:add_acl(Login, Topic, erlang:binary_to_atom(Action, utf8), erlang:binary_to_atom(Access, utf8));
         Err -> Err
     end,
-    add_acl(ParamsN, [{Login, format_msg(Re)} | ReList]);   
+    maps:merge(#{topic => Topic,
+                 action => Action,
+                 access => Access,
+                 result => format_msg(Re)
+                }, case Login of
+                     all -> #{all => '$all'};
+                     _ -> maps:from_list([Login])
+                   end).
     
-add_acl([], ReList) ->
-    {ok, ReList}.
-
-delete(#{login := Login, topic := Topic}, _) ->
-    return(emqx_auth_mnesia_cli:remove_acl(urldecode(Login), urldecode(Topic))).
+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.
-

+ 198 - 0
apps/emqx_auth_mnesia/src/emqx_acl_mnesia_cli.erl

@@ -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"}
+                   ]).
+
+

+ 0 - 24
apps/emqx_auth_mnesia/src/emqx_auth_mnesia.app.src.script

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

+ 32 - 24
apps/emqx_auth_mnesia/src/emqx_auth_mnesia.erl

@@ -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>>).

+ 200 - 82
apps/emqx_auth_mnesia/src/emqx_auth_mnesia_api.erl

@@ -17,100 +17,206 @@
 -module(emqx_auth_mnesia_api).
 
 -include_lib("stdlib/include/qlc.hrl").
+-include_lib("stdlib/include/ms_transform.hrl").
 
--import(proplists, [get_value/2]).
+-define(TABLE, emqx_user).
 
+-import(proplists, [get_value/2]).
 -import(minirest,  [return/1]).
+-export([paginate/5]).
+
+-export([ list_clientid/2
+        , lookup_clientid/2
+        , add_clientid/2
+        , update_clientid/2
+        , delete_clientid/2
+        ]).
 
--rest_api(#{name   => list_emqx_user,
+-rest_api(#{name   => list_clientid,
             method => 'GET',
-            path   => "/mqtt_user",
-            func   => list,
-            descr  => "List available mnesia in the cluster"
+            path   => "/auth_clientid",
+            func   => list_clientid,
+            descr  => "List available clientid in the cluster"
            }).
 
--rest_api(#{name   => lookup_emqx_user,
+-rest_api(#{name   => lookup_clientid,
             method => 'GET',
-            path   => "/mqtt_user/:bin:login",
-            func   => lookup,
-            descr  => "Lookup mnesia in the cluster"
+            path   => "/auth_clientid/:bin:clientid",
+            func   => lookup_clientid,
+            descr  => "Lookup clientid in the cluster"
            }).
 
--rest_api(#{name   => add_emqx_user,
+-rest_api(#{name   => add_clientid,
             method => 'POST',
-            path   => "/mqtt_user",
-            func   => add,
-            descr  => "Add mnesia in the cluster"
+            path   => "/auth_clientid",
+            func   => add_clientid,
+            descr  => "Add clientid in the cluster"
            }).
 
--rest_api(#{name   => update_emqx_user,
+-rest_api(#{name   => update_clientid,
             method => 'PUT',
-            path   => "/mqtt_user/:bin:login",
-            func   => update,
-            descr  => "Update mnesia in the cluster"
+            path   => "/auth_clientid/:bin:clientid",
+            func   => update_clientid,
+            descr  => "Update clientid in the cluster"
            }).
 
--rest_api(#{name   => delete_emqx_user,
+-rest_api(#{name   => delete_clientid,
             method => 'DELETE',
-            path   => "/mqtt_user/:bin:login",
-            func   => delete,
-            descr  => "Delete mnesia in the cluster"
+            path   => "/auth_clientid/:bin:clientid",
+            func   => delete_clientid,
+            descr  => "Delete clientid in the cluster"
            }).
 
--export([ list/2
-        , lookup/2
-        , add/2
-        , update/2
-        , delete/2
+-export([ list_username/2
+        , lookup_username/2
+        , add_username/2
+        , update_username/2
+        , delete_username/2
         ]).
 
--export([paginate/3]).
+-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(_Bindings, Params) ->
-    return({ok, paginate(emqx_user, Params, fun format/1)}).
+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(#{login := Login}, _Params) ->
-    return({ok, format(emqx_auth_mnesia_cli:lookup_user(urldecode(Login)))}).
+lookup_clientid(#{clientid := Clientid}, _Params) ->
+    return({ok, format(emqx_auth_mnesia_cli:lookup_user({clientid, urldecode(Clientid)}))}).
 
-add(_Bindings, Params) ->
+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)),
+do_add_clientid([ Params | ParamsN ], ReList ) ->
+    Clientid = urldecode(get_value(<<"clientid">>, Params)),
+    do_add_clientid(ParamsN, [{Clientid, format_msg(do_add_clientid(Params))} | ReList]);
+
+do_add_clientid([], ReList) ->
+    {ok, ReList}.
+
+do_add_clientid(Params) ->
+    Clientid = urldecode(get_value(<<"clientid">>, Params)),
     Password = urldecode(get_value(<<"password">>, Params)),
-    IsSuperuser = get_value(<<"is_superuser">>, Params),
-    Re = case validate([login, password, is_superuser], [Login, Password, IsSuperuser]) of
+    Login = {clientid, Clientid},
+    case validate([login, password], [Login, Password]) of
         ok -> 
-            emqx_auth_mnesia_cli:add_user(Login, Password, IsSuperuser);
+            emqx_auth_mnesia_cli:add_user(Login, Password);
         Err -> Err
-    end,
-    add_user(ParamsN, [{Login, format_msg(Re)} | ReList]);   
-    
-add_user([], ReList) ->
+    end.
+
+update_clientid(#{clientid := Clientid}, Params) ->
+    Password = get_value(<<"password">>, Params),
+    case validate([password], [Password]) of
+        ok -> return(emqx_auth_mnesia_cli:update_user({clientid, urldecode(Clientid)}, urldecode(Password)));
+        Err -> return(Err)
+    end.
+
+delete_clientid(#{clientid := Clientid}, _) ->
+    return(emqx_auth_mnesia_cli:remove_user({clientid, urldecode(Clientid)})).
+
+%%------------------------------------------------------------------------------
+%% Auth Username Api
+%%------------------------------------------------------------------------------
+
+list_username(_Bindings, Params) ->
+    MatchSpec = ets:fun2ms(fun({?TABLE, {username, Username}, Password, CreatedAt}) -> {?TABLE, {username, Username}, Password, CreatedAt} end),
+    return({ok, paginate(?TABLE, MatchSpec, Params, fun emqx_auth_mnesia_cli:comparing/2, fun({?TABLE, {username, X}, _, _}) -> #{username => X} end)}).
+
+lookup_username(#{username := Username}, _Params) ->
+    return({ok, format(emqx_auth_mnesia_cli:lookup_user({username, urldecode(Username)}))}).
+
+add_username(_Bindings, Params) ->
+    [ P | _] = Params,
+    case is_list(P) of
+        true -> return(do_add_username(Params, []));
+        false ->
+            case do_add_username(Params) of
+                ok -> return(ok);
+                <<"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}.
 
-update(#{login := Login}, Params) ->
+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),
-    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({username, urldecode(Username)}, urldecode(Password)));
         Err -> return(Err)
     end.
 
-delete(#{login := Login}, _) ->
-    return(emqx_auth_mnesia_cli:remove_user(urldecode(Login))).
+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]}.
-
-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]).
-
-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]).
+      data  => [RowFun(Row) || Row <- lists:sort(ComparingFun, Rows)]}.
+
+query_handle(Table, MatchSpec) when is_atom(Table) ->
+    Options = {traverse, {select, MatchSpec}},
+    qlc:q([R|| R <- ets:table(Table, Options)]);
+query_handle([Table], MatchSpec) when is_atom(Table) ->
+    Options = {traverse, {select, MatchSpec}},
+    qlc:q([R|| R <- ets:table(Table, Options)]);
+query_handle(Tables, MatchSpec) ->
+    Options = {traverse, {select, MatchSpec}},
+    qlc:append([qlc:q([E || E <- ets:table(T, Options)]) || T <- Tables]).
+
+count(Table, MatchSpec) when is_atom(Table) ->
+    [{MatchPattern, Where, _Re}] = MatchSpec,
+    NMatchSpec = [{MatchPattern, Where, [true]}],
+    ets:select_count(Table, NMatchSpec);
+count([Table], MatchSpec) when is_atom(Table) ->
+    [{MatchPattern, Where, _Re}] = MatchSpec,
+    NMatchSpec = [{MatchPattern, Where, [true]}],
+    ets:select_count(Table, NMatchSpec);
+count(Tables, MatchSpec) ->
+    lists:sum([count(T, MatchSpec) || T <- Tables]).
 
 page(Params) ->
     binary_to_integer(proplists:get_value(<<"_page">>, Params, <<"1">>)).
@@ -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([]) ->
-    #{};
+format({?TABLE, {username, Username}, Password, _InterTime}) ->
+    #{username => Username,
+      password => Password};
 
-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([]) ->
+    #{}.
 
 validate([], []) ->
     ok;
@@ -173,13 +290,14 @@ 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(password, V) when is_binary(V)
+do_validation(login, {username, V}) when is_binary(V)
                      andalso byte_size(V) > 0 ->
     true;
-do_validation(is_superuser, V) when is_boolean(V) ->
+do_validation(password, V) when is_binary(V)
+                     andalso byte_size(V) > 0 ->
     true;
 do_validation(_, _) ->
     false.

+ 13 - 13
apps/emqx_auth_mnesia/src/emqx_auth_mnesia_app.erl

@@ -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, [#{}]).

+ 79 - 91
apps/emqx_auth_mnesia/src/emqx_auth_mnesia_cli.erl

@@ -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
-         ok -> emqx_ctl:print("ok~n");
+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)) ];
-
-acl_cli(["list"]) ->
-    [emqx_ctl:print("Acl(login = ~p)~n",[E])
-     || E <- all_acls() ];
+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(_) ->
-    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"}]).

+ 97 - 161
apps/emqx_auth_mnesia/test/emqx_acl_mnesia_SUITE.erl

@@ -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()),
-
-    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),
-    
-    ?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),
-
-    User1 = #{zone => external, clientid => <<"test_clientid">>},
-    User2 = #{zone => external, clientid => <<"no_exist">>},
-
-    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),
-
-    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">>),
-
-    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">>},
+    ?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/A/B">>, <<"pubsub">>, false),
+    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),
 
-    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">>),
+    ?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())),
 
-    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(_) ->
+    User1 = #{zone => external, clientid => <<"test_clientid">>},
+    User2 = #{zone => external, clientid => <<"no_exist">>, username => <<"test_username">>},
+    User3 = #{zone => external, clientid => <<"test_clientid">>, username => <<"test_username">>},
+    allow = emqx_access_control:check_acl(User1, subscribe, <<"topic/test_clientid">>),
+    deny  = emqx_access_control:check_acl(User1, publish,   <<"topic/A">>),
+    deny  = emqx_access_control:check_acl(User2, subscribe, <<"topic/test_username">>),
+    allow = emqx_access_control:check_acl(User2, publish,   <<"topic/A">>),
+    allow = emqx_access_control:check_acl(User3, subscribe, <<"topic/test_clientid">>),
+    deny  = emqx_access_control:check_acl(User3, subscribe, <<"topic/test_username">>),
+    deny  = emqx_access_control:check_acl(User3, publish,   <<"topic/A">>),
+    deny  = emqx_access_control:check_acl(User3, subscribe, <<"topic/A/B">>),
+    deny  = emqx_access_control:check_acl(User3, publish,   <<"topic/A/B">>),
+
+    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, <<"#">>),
+
+    ?assertEqual([], emqx_acl_mnesia_cli:all_acls()).
+
+t_acl_cli(_Config) ->
     meck:new(emqx_ctl, [non_strict, passthrough]),
     meck:expect(emqx_ctl, print, fun(Arg) -> emqx_ctl:format(Arg) end),
     meck:expect(emqx_ctl, print, fun(Msg, Arg) -> emqx_ctl:format(Msg, Arg) end),
@@ -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"]))),
+
+    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"])),
 
-    ?assertMatch({match, _}, re:run(emqx_auth_mnesia_cli:acl_cli([]), "mqtt-acl")),
+    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) ->

+ 134 - 109
apps/emqx_auth_mnesia/test/emqx_auth_mnesia_SUITE.erl

@@ -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: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)),
+
+    ?assertEqual(2,length(emqx_auth_mnesia_cli:all_users())),
 
-    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:update_user({username,?USERNAME}, ?NPASSWORD),
+    {error,noexisted} = emqx_auth_mnesia_cli:update_user({username, <<"no_existed_user">>}, ?PASSWORD),
 
-    [<<"test_username">>] = emqx_auth_mnesia_cli:all_users(),
-    [{emqx_user, <<"test_username">>, _HashedPass, false}] =
-        emqx_auth_mnesia_cli:lookup_user(<<"test_username">>),
+    ok = emqx_auth_mnesia_cli:update_user({clientid,?CLIENTID}, ?NPASSWORD),
+    {error,noexisted} = emqx_auth_mnesia_cli:update_user({clientid, <<"no_existed_user">>}, ?PASSWORD),
 
-    User1 = #{username => <<"test_username">>,
-              password => <<"new_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),
+    {ok, #{auth_result := success,
+           anonymous := false}} = emqx_access_control:authenticate(User2),
 
-    {error,password_error} = emqx_access_control:authenticate(User1#{password => <<"error_password">>}),
+    {error,password_error} = emqx_access_control:authenticate(User2#{password => <<"error_password">>}),
 
-    ok = emqx_auth_mnesia_cli:remove_user(<<"test_clientid">>),
+    ok = emqx_auth_mnesia_cli:remove_user({clientid,?CLIENTID}),
     {ok, #{auth_result := success,
-           anonymous := true }} = emqx_access_control:authenticate(User1).
+           anonymous := true }} = emqx_access_control:authenticate(User2),
+
+    [] = emqx_auth_mnesia_cli:all_users().
 
-t_rest_api(_Config) ->
+t_auth_clientid_cli(_) ->
     clean_all_users(),
 
-    {ok, Result1} = request_http_rest_list(),
-    [] = get_http_data(Result1),
+    HashType = application:get_env(emqx_auth_mnesia, password_hash, sha256),
 
-    Params = #{<<"login">> => <<"test_username">>, <<"password">> => <<"password">>, <<"is_superuser">> => true},
-    {ok, _} = request_http_rest_add(Params),
+    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>>)),
 
-    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}
-                ],
-    {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),
+    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>>)),
 
-    {ok, Result3} = request_http_rest_lookup(<<"test_username">>),
-    #{<<"login">> := <<"test_username">>, <<"is_superuser">> := true} = get_http_data(Result3),
+    emqx_auth_mnesia_cli:auth_clientid_cli(["del", ?CLIENTID]),
+    ?assertEqual([], emqx_auth_mnesia_cli:lookup_user(?CLIENTID)),
 
-    {ok, _} = request_http_rest_update(<<"test_username">>, <<"new_password">>, error_format),
-    {ok, _} = request_http_rest_update(<<"error_username">>, <<"new_password">>, false),
+    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"]))),
 
-    {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),
+    emqx_auth_mnesia_cli:auth_clientid_cli(usage).
 
-    User1 = #{username => <<"test_username">>,
-        password => <<"new_password">>,
-        zone     => external},
+t_auth_username_cli(_) ->
+    clean_all_users(),
 
-    {ok, #{is_superuser := false, 
-        auth_result := success,
-        anonymous := false}} = emqx_access_control:authenticate(User1),
+    HashType = application:get_env(emqx_auth_mnesia, password_hash, sha256),
 
-    {ok, _} = request_http_rest_delete(<<"test_username">>),
-    {ok, #{auth_result := success,
-           anonymous := true }} = emqx_access_control:authenticate(User1).
+    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>>)),
 
-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">>)),
+    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>>)),
 
-    ?assertEqual(ok, emqx_ctl:run_command(["mqtt-user", "update", "TestUser", "NewPassword", true])),
-    ?assertMatch([{emqx_user, <<"TestUser">>, _, true}], emqx_auth_mnesia_cli:lookup_user(<<"TestUser">>)),
+    emqx_auth_mnesia_cli:auth_username_cli(["del", ?USERNAME]),
+    ?assertEqual([], emqx_auth_mnesia_cli:lookup_user(?USERNAME)),
 
-    ?assertEqual(ok, emqx_ctl:run_command(["mqtt-user", "del", "TestUser"])),
-    ?assertMatch([], emqx_auth_mnesia_cli:lookup_user(<<"TestUser">>)),
+    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"]))),
 
-    ?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"])).
+    emqx_auth_mnesia_cli:auth_username_cli(usage).
 
-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),
 
+t_clientid_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_clientid"]),
+    [] = get_http_data(Result1),
+
+    Params1 = #{<<"clientid">> => ?CLIENTID, <<"password">> => ?PASSWORD},
+    {ok, _} = request_http_rest_add(["auth_clientid"], Params1),
+
+    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, Result3} = request_http_rest_add(["auth_clientid"], Params3),
+    ?assertMatch(#{ ?CLIENTID := <<"{error,existed}">>
+                  , <<"clientid1">> := <<"ok">>
+                  , <<"clientid2">> := <<"ok">>
+                  }, get_http_data(Result3)),
+
+    {ok, Result4} = request_http_rest_list(["auth_clientid"]),
+    ?assertEqual(3, length(get_http_data(Result4))),
+
+    {ok, _} = request_http_rest_delete(["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(),
+
+    {ok, Result1} = request_http_rest_list(["auth_username"]),
+    [] = get_http_data(Result1),
+
+    Params1 = #{<<"username">> => ?USERNAME, <<"password">> => ?PASSWORD},
+    {ok, _} = request_http_rest_add(["auth_username"], Params1),
 
-    ?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")),
+    Params2 = #{<<"username">> => ?USERNAME, <<"password">> => ?NPASSWORD},
+    {ok, _} = request_http_rest_update(["auth_username/" ++ binary_to_list(?USERNAME)], Params2),
 
-    ?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"])),
+    {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(["del", "TestUser"]), "ok")),
-    ?assertMatch([], emqx_auth_mnesia_cli:auth_cli(["show", "TestUser"])),
-    ?assertMatch([], emqx_auth_mnesia_cli:auth_cli(["list"])),
+    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)),
 
-    ?assertMatch({match, _}, re:run(emqx_auth_mnesia_cli:auth_cli([]), "mqtt-user")),
+    {ok, Result4} = request_http_rest_list(["auth_username"]),
+    ?assertEqual(3, length(get_http_data(Result4))),
 
-    meck:unload(emqx_ctl).
+    {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) ->

+ 0 - 59
apps/emqx_auth_mongo/.github/workflows/run_test_cases.yaml

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

+ 0 - 24
apps/emqx_auth_mongo/src/emqx_auth_mongo.app.src.script

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

+ 0 - 37
apps/emqx_auth_mongo/src/emqx_auth_mongo.appup.src

@@ -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]}
-     ]}
-   ]
-}.

+ 0 - 59
apps/emqx_auth_mysql/.github/workflows/run_test_cases.yaml

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

+ 0 - 24
apps/emqx_auth_mysql/src/emqx_auth_mysql.app.src.script

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

+ 0 - 25
apps/emqx_auth_mysql/src/emqx_auth_mysql.appup.src

@@ -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, []}
-     ]}
-   ]
-}.

+ 0 - 30
apps/emqx_auth_pgsql/.ci/docker-compose.yml

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

+ 0 - 8
apps/emqx_auth_pgsql/.ci/pgsql/Dockerfile

@@ -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"]

+ 0 - 51
apps/emqx_auth_pgsql/.github/workflows/run_test_cases.yaml

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

+ 0 - 24
apps/emqx_auth_pgsql/src/emqx_auth_pgsql.app.src.script

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

+ 0 - 25
apps/emqx_auth_pgsql/src/emqx_auth_pgsql.appup.src

@@ -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, []}
-     ]}
-   ]
-}.

+ 0 - 31
apps/emqx_auth_redis/.ci/docker-compose-tls.yml

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

+ 0 - 25
apps/emqx_auth_redis/.ci/docker-compose.yml

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

+ 0 - 80
apps/emqx_auth_redis/.github/workflows/run_test_cases.yaml

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

+ 0 - 24
apps/emqx_auth_redis/src/emqx_auth_redis.app.src.script

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

+ 0 - 36
apps/emqx_auth_redis/src/emqx_auth_redis.appup.src

@@ -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, []}
-               ]
-     }
-   ]
-}.

+ 0 - 28
apps/emqx_bridge_mqtt/.github/workflows/run_test_cases.yaml

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

+ 0 - 24
apps/emqx_bridge_mqtt/src/emqx_bridge_mqtt.app.src.script

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

+ 0 - 32
apps/emqx_bridge_mqtt/src/emqx_bridge_mqtt.appup.src

@@ -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, []}}
-    ]}
-  ]
-}.

+ 0 - 29
apps/emqx_coap/.github/workflows/run_test_cases.yaml

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

+ 0 - 24
apps/emqx_coap/src/emqx_coap.app.src.script

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

+ 0 - 29
apps/emqx_dashboard/.github/workflows/run_test_cases.yaml

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

+ 0 - 24
apps/emqx_dashboard/src/emqx_dashboard.app.src.script

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

+ 0 - 18
apps/emqx_dashboard/src/emqx_dashboard.appup.src

@@ -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, []}}
-   ]}
- ]
-}.

+ 0 - 31
apps/emqx_exhook/.github/workflows/run_test_cases.yaml

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

+ 0 - 38
apps/emqx_exproto/.github/workflows/run_test_case.yaml

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

+ 0 - 29
apps/emqx_lua_hook/.github/workflows/run_test_cases.yaml

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

+ 0 - 24
apps/emqx_lua_hook/src/emqx_lua_hook.app.src.script

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

+ 0 - 29
apps/emqx_lwm2m/.github/workflows/run_test_cases.yaml

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

+ 0 - 24
apps/emqx_lwm2m/src/emqx_lwm2m.app.src.script

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

+ 0 - 30
apps/emqx_management/.github/workflows/run_test_cases.yaml

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

+ 0 - 24
apps/emqx_management/src/emqx_management.app.src.script

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

+ 0 - 30
apps/emqx_management/src/emqx_management.appup.src

@@ -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, []}
-    ]}
-  ]
-}.

+ 0 - 88
apps/emqx_passwd/.github/workflows/run_test_cases.yaml

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

+ 0 - 14
apps/emqx_passwd/src/emqx_passwd.app.src.script

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

+ 0 - 28
apps/emqx_plugin_template/.github/workflows/run_test_cases.yaml

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

+ 0 - 24
apps/emqx_plugin_template/src/emqx_plugin_template.app.src.script

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

+ 0 - 29
apps/emqx_prometheus/.github/workflows/run_test_cases.yaml

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

+ 0 - 24
apps/emqx_prometheus/src/emqx_prometheus.app.src.script

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

+ 0 - 29
apps/emqx_psk_file/.github/workflows/run_test_cases.yaml

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

+ 0 - 24
apps/emqx_psk_file/src/emqx_psk_file.app.src.script

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

+ 0 - 29
apps/emqx_recon/.github/workflows/run_test_cases.yaml

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

+ 0 - 24
apps/emqx_recon/src/emqx_recon.app.src.script

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

+ 0 - 29
apps/emqx_retainer/.github/workflows/run_test_cases.yaml

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

+ 0 - 24
apps/emqx_retainer/src/emqx_retainer.app.src.script

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

+ 0 - 29
apps/emqx_rule_engine/.github/workflows/run_test_cases.yaml

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

+ 0 - 24
apps/emqx_rule_engine/src/emqx_rule_engine.app.src.script

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

+ 0 - 44
apps/emqx_rule_engine/src/emqx_rule_engine.appup.src

@@ -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, []}
-   ]}
- ]
-}.

+ 0 - 31
apps/emqx_sasl/.github/workflows/run_test_case.yaml

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

+ 0 - 33
apps/emqx_sasl/src/emqx_sasl.app.src.script

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

+ 0 - 30
apps/emqx_sasl/src/emqx_sasl.appup.src

@@ -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, []}
-   ]}
- ]
-}.

+ 0 - 31
apps/emqx_sn/.github/workflows/run_test_cases.yaml

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

+ 0 - 24
apps/emqx_sn/src/emqx_sn.app.src.script

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

+ 0 - 25
apps/emqx_sn/src/emqx_sn.appup.src

@@ -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, []}
-    ]}
-  ]
-}.

+ 0 - 29
apps/emqx_stomp/.github/workflows/run_test_cases.yaml

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

+ 0 - 24
apps/emqx_stomp/src/emqx_stomp.app.src.script

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

+ 0 - 24
apps/emqx_telemetry/src/emqx_telemetry.appup.src

@@ -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, []}
-    ]}
-  ]
-}.

+ 0 - 33
apps/emqx_web_hook/.github/workflows/run_test_cases.yaml

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

+ 0 - 24
apps/emqx_web_hook/src/emqx_web_hook.app.src.script

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

+ 0 - 36
apps/emqx_web_hook/src/emqx_web_hook.appup.src

@@ -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, []}}
-   ]}
- ]
-}.

+ 9 - 4
sync-apps.sh

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