refactor: close managment http 8081 (#5564)
This commit is contained in:
parent
4c1e0802d9
commit
86231f795d
|
@ -48,7 +48,7 @@ emqx_test(){
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
IDLE_TIME=0
|
IDLE_TIME=0
|
||||||
while ! curl http://localhost:8081/api/v5/status >/dev/null 2>&1; do
|
while ! curl http://localhost:18083/api/v5/status >/dev/null 2>&1; do
|
||||||
if [ $IDLE_TIME -gt 10 ]
|
if [ $IDLE_TIME -gt 10 ]
|
||||||
then
|
then
|
||||||
echo "emqx running error"
|
echo "emqx running error"
|
||||||
|
@ -139,7 +139,7 @@ EOF
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
IDLE_TIME=0
|
IDLE_TIME=0
|
||||||
while ! curl http://localhost:8081/api/v5/status >/dev/null 2>&1; do
|
while ! curl http://localhost:18083/api/v5/status >/dev/null 2>&1; do
|
||||||
if [ $IDLE_TIME -gt 10 ]
|
if [ $IDLE_TIME -gt 10 ]
|
||||||
then
|
then
|
||||||
echo "emqx running error"
|
echo "emqx running error"
|
||||||
|
@ -168,7 +168,7 @@ EOF
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
IDLE_TIME=0
|
IDLE_TIME=0
|
||||||
while ! curl http://localhost:8081/api/v5/status >/dev/null 2>&1; do
|
while ! curl http://localhost:18083/api/v5/status >/dev/null 2>&1; do
|
||||||
if [ $IDLE_TIME -gt 10 ]
|
if [ $IDLE_TIME -gt 10 ]
|
||||||
then
|
then
|
||||||
echo "emqx service error"
|
echo "emqx service error"
|
||||||
|
|
|
@ -33,7 +33,7 @@ defaults
|
||||||
frontend emqx_mgmt
|
frontend emqx_mgmt
|
||||||
mode tcp
|
mode tcp
|
||||||
option tcplog
|
option tcplog
|
||||||
bind *:8081
|
bind *:18083
|
||||||
default_backend emqx_mgmt_back
|
default_backend emqx_mgmt_back
|
||||||
|
|
||||||
frontend emqx_dashboard
|
frontend emqx_dashboard
|
||||||
|
@ -45,8 +45,8 @@ frontend emqx_dashboard
|
||||||
backend emqx_mgmt_back
|
backend emqx_mgmt_back
|
||||||
mode http
|
mode http
|
||||||
# balance static-rr
|
# balance static-rr
|
||||||
server emqx-1 node1.emqx.io:8081
|
server emqx-1 node1.emqx.io:18083
|
||||||
server emqx-2 node2.emqx.io:8081
|
server emqx-2 node2.emqx.io:18083
|
||||||
|
|
||||||
backend emqx_dashboard_back
|
backend emqx_dashboard_back
|
||||||
mode http
|
mode http
|
||||||
|
|
|
@ -197,7 +197,7 @@ jobs:
|
||||||
./emqx/bin/emqx start || cat emqx/log/erlang.log.1
|
./emqx/bin/emqx start || cat emqx/log/erlang.log.1
|
||||||
ready='no'
|
ready='no'
|
||||||
for i in {1..10}; do
|
for i in {1..10}; do
|
||||||
if curl -fs 127.0.0.1:8081/api/v5/status > /dev/null; then
|
if curl -fs 127.0.0.1:18083/api/v5/status > /dev/null; then
|
||||||
ready='yes'
|
ready='yes'
|
||||||
break
|
break
|
||||||
fi
|
fi
|
||||||
|
|
|
@ -113,7 +113,7 @@ jobs:
|
||||||
./emqx/bin/emqx start || cat emqx/log/erlang.log.1
|
./emqx/bin/emqx start || cat emqx/log/erlang.log.1
|
||||||
ready='no'
|
ready='no'
|
||||||
for i in {1..10}; do
|
for i in {1..10}; do
|
||||||
if curl -fs 127.0.0.1:8081/api/v5/status > /dev/null; then
|
if curl -fs 127.0.0.1:18083/api/v5/status > /dev/null; then
|
||||||
ready='yes'
|
ready='yes'
|
||||||
break
|
break
|
||||||
fi
|
fi
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
[{deps,
|
[{deps,
|
||||||
[ meck
|
[ meck
|
||||||
, {bbmustache,"1.10.0"}
|
, {bbmustache,"1.10.0"}
|
||||||
, {emqx_ct_helpers, {git,"https://github.com/emqx/emqx-ct-helpers", {branch,"hocon"}}}
|
, {emqx_ct_helpers, {git,"https://github.com/emqx/emqx-ct-helpers.git", {branch,"hocon"}}}
|
||||||
, {emqtt, {git, "https://github.com/emqx/emqtt", {tag, "1.4.2"}}}
|
, {emqtt, {git, "https://github.com/emqx/emqtt", {tag, "1.4.2"}}}
|
||||||
]},
|
]},
|
||||||
{extra_src_dirs, [{"test",[recursive]}]}
|
{extra_src_dirs, [{"test",[recursive]}]}
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
|
|
||||||
{profiles,
|
{profiles,
|
||||||
[{test, [
|
[{test, [
|
||||||
{deps, [ {emqx_ct_helper, {git, "https://github.com/emqx/emqx-ct-helpers", {tag, "v1.1.4"}}}
|
{deps, [{emqx_ct_helpers, {git,"https://github.com/emqx/emqx-ct-helpers.git", {branch,"hocon"}}}
|
||||||
]}
|
]}
|
||||||
]}
|
]}
|
||||||
]}.
|
]}.
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
|
|
||||||
{profiles,
|
{profiles,
|
||||||
[{test, [
|
[{test, [
|
||||||
{deps, [ {emqx_ct_helper, {git, "https://github.com/emqx/emqx-ct-helpers", {tag, "v1.1.4"}}}
|
{deps, [{emqx_ct_helpers, {git,"https://github.com/emqx/emqx-ct-helpers.git", {branch,"hocon"}}}
|
||||||
]}
|
]}
|
||||||
]}
|
]}
|
||||||
]}.
|
]}.
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
, auth_header/2
|
, auth_header/2
|
||||||
]).
|
]).
|
||||||
|
|
||||||
-define(HOST, "http://127.0.0.1:8081/").
|
-define(HOST, "http://127.0.0.1:18083/").
|
||||||
-define(API_VERSION, "v5").
|
-define(API_VERSION, "v5").
|
||||||
-define(BASE_PATH, "api").
|
-define(BASE_PATH, "api").
|
||||||
|
|
||||||
|
@ -100,11 +100,9 @@ init_per_suite(Config) ->
|
||||||
meck:expect(emqx_resource, health_check, fun(_) -> ok end),
|
meck:expect(emqx_resource, health_check, fun(_) -> ok end),
|
||||||
meck:expect(emqx_resource, remove, fun(_) -> ok end ),
|
meck:expect(emqx_resource, remove, fun(_) -> ok end ),
|
||||||
|
|
||||||
ekka_mnesia:start(),
|
|
||||||
emqx_mgmt_auth:mnesia(boot),
|
|
||||||
|
|
||||||
ok = emqx_config:init_load(emqx_authz_schema, ?CONF_DEFAULT),
|
ok = emqx_config:init_load(emqx_authz_schema, ?CONF_DEFAULT),
|
||||||
ok = emqx_ct_helpers:start_apps([emqx_management, emqx_authz], fun set_special_configs/1),
|
|
||||||
|
ok = emqx_ct_helpers:start_apps([emqx_authz, emqx_dashboard], fun set_special_configs/1),
|
||||||
{ok, _} = emqx:update_config([authorization, cache, enable], false),
|
{ok, _} = emqx:update_config([authorization, cache, enable], false),
|
||||||
{ok, _} = emqx:update_config([authorization, no_match], deny),
|
{ok, _} = emqx:update_config([authorization, no_match], deny),
|
||||||
|
|
||||||
|
@ -112,13 +110,20 @@ init_per_suite(Config) ->
|
||||||
|
|
||||||
end_per_suite(_Config) ->
|
end_per_suite(_Config) ->
|
||||||
{ok, _} = emqx_authz:update(replace, []),
|
{ok, _} = emqx_authz:update(replace, []),
|
||||||
emqx_ct_helpers:stop_apps([emqx_resource, emqx_authz, emqx_management]),
|
emqx_ct_helpers:stop_apps([emqx_resource, emqx_authz, emqx_dashboard]),
|
||||||
meck:unload(emqx_resource),
|
meck:unload(emqx_resource),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
set_special_configs(emqx_management) ->
|
set_special_configs(emqx_dashboard) ->
|
||||||
emqx_config:put([emqx_management], #{listeners => [#{protocol => http, port => 8081}],
|
Config = #{
|
||||||
applications =>[#{id => "admin", secret => "public"}]}),
|
default_username => <<"admin">>,
|
||||||
|
default_password => <<"public">>,
|
||||||
|
listeners => [#{
|
||||||
|
protocol => http,
|
||||||
|
port => 18083
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
emqx_config:put([emqx_dashboard], Config),
|
||||||
ok;
|
ok;
|
||||||
set_special_configs(emqx_authz) ->
|
set_special_configs(emqx_authz) ->
|
||||||
emqx_config:put([authorization_rules], #{rules => []}),
|
emqx_config:put([authorization_rules], #{rules => []}),
|
||||||
|
@ -225,8 +230,8 @@ t_move_rule(_) ->
|
||||||
|
|
||||||
request(Method, Url, Body) ->
|
request(Method, Url, Body) ->
|
||||||
Request = case Body of
|
Request = case Body of
|
||||||
[] -> {Url, [auth_header("admin", "public")]};
|
[] -> {Url, [auth_header_()]};
|
||||||
_ -> {Url, [auth_header("admin", "public")], "application/json", jsx:encode(Body)}
|
_ -> {Url, [auth_header_()], "application/json", jsx:encode(Body)}
|
||||||
end,
|
end,
|
||||||
ct:pal("Method: ~p, Request: ~p", [Method, Request]),
|
ct:pal("Method: ~p, Request: ~p", [Method, Request]),
|
||||||
case httpc:request(Method, Request, [], [{body_format, binary}]) of
|
case httpc:request(Method, Request, [], [{body_format, binary}]) of
|
||||||
|
@ -245,3 +250,9 @@ uri(Parts) when is_list(Parts) ->
|
||||||
|
|
||||||
get_rules(Result) ->
|
get_rules(Result) ->
|
||||||
maps:get(<<"rules">>, jsx:decode(Result), []).
|
maps:get(<<"rules">>, jsx:decode(Result), []).
|
||||||
|
|
||||||
|
auth_header_() ->
|
||||||
|
Username = <<"admin">>,
|
||||||
|
Password = <<"public">>,
|
||||||
|
{ok, Token} = emqx_dashboard_admin:sign_token(Username, Password),
|
||||||
|
{"Authorization", "Bearer " ++ binary_to_list(Token)}.
|
||||||
|
|
|
@ -33,7 +33,7 @@ fields("emqx_dashboard") ->
|
||||||
|
|
||||||
fields("http") ->
|
fields("http") ->
|
||||||
[ {"protocol", hoconsc:enum([http, https])}
|
[ {"protocol", hoconsc:enum([http, https])}
|
||||||
, {"port", emqx_schema:t(integer(), undefined, 8081)}
|
, {"port", emqx_schema:t(integer(), undefined, 18083)}
|
||||||
, {"num_acceptors", emqx_schema:t(integer(), undefined, 4)}
|
, {"num_acceptors", emqx_schema:t(integer(), undefined, 4)}
|
||||||
, {"max_connections", emqx_schema:t(integer(), undefined, 512)}
|
, {"max_connections", emqx_schema:t(integer(), undefined, 512)}
|
||||||
, {"backlog", emqx_schema:t(integer(), undefined, 1024)}
|
, {"backlog", emqx_schema:t(integer(), undefined, 1024)}
|
||||||
|
|
|
@ -51,7 +51,6 @@
|
||||||
, emqx_auto_subscribe_schema
|
, emqx_auto_subscribe_schema
|
||||||
, emqx_bridge_mqtt_schema
|
, emqx_bridge_mqtt_schema
|
||||||
, emqx_modules_schema
|
, emqx_modules_schema
|
||||||
, emqx_management_schema
|
|
||||||
, emqx_dashboard_schema
|
, emqx_dashboard_schema
|
||||||
, emqx_gateway_schema
|
, emqx_gateway_schema
|
||||||
, emqx_prometheus_schema
|
, emqx_prometheus_schema
|
||||||
|
|
|
@ -1,42 +0,0 @@
|
||||||
emqx_management {
|
|
||||||
applications = [
|
|
||||||
{
|
|
||||||
id = "admin",
|
|
||||||
secret = "public"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
max_row_limit = 10000
|
|
||||||
listeners = [
|
|
||||||
{
|
|
||||||
num_acceptors = 4
|
|
||||||
max_connections = 512
|
|
||||||
protocol = http
|
|
||||||
port = 8081
|
|
||||||
backlog = 512
|
|
||||||
send_timeout = 15s
|
|
||||||
send_timeout_close = true
|
|
||||||
inet6 = false
|
|
||||||
ipv6_v6only = false
|
|
||||||
}
|
|
||||||
## ,
|
|
||||||
## {
|
|
||||||
## protocol: https
|
|
||||||
## port: 8081
|
|
||||||
## acceptors: 2
|
|
||||||
## backlog: 512
|
|
||||||
## send_timeout: 15s
|
|
||||||
## send_timeout_close: true
|
|
||||||
## inet6: false
|
|
||||||
## ipv6_v6only: false
|
|
||||||
## certfile = "etc/certs/cert.pem"
|
|
||||||
## keyfile = "etc/certs/key.pem"
|
|
||||||
## cacertfile = "etc/certs/cacert.pem"
|
|
||||||
## verify = verify_peer
|
|
||||||
## tls_versions = "tlsv1.3,tlsv1.2,tlsv1.1,tlsv1"
|
|
||||||
## ciphers = "TLS_AES_256_GCM_SHA384,TLS_AES_128_GCM_SHA256,TLS_CHACHA20_POLY1305_SHA256,TLS_AES_128_CCM_SHA256,TLS_AES_128_CCM_8_SHA256,ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384,ECDHE-ECDSA-AES256-SHA384,ECDHE-RSA-AES256-SHA384,ECDHE-ECDSA-DES-CBC3-SHA,ECDH-ECDSA-AES256-GCM-SHA384,ECDH-RSA-AES256-GCM-SHA384,ECDH-ECDSA-AES256-SHA384,ECDH-RSA-AES256-SHA384,DHE-DSS-AES256-GCM-SHA384,DHE-DSS-AES256-SHA256,AES256-GCM-SHA384,AES256-SHA256,ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-RSA-AES128-GCM-SHA256,ECDHE-ECDSA-AES128-SHA256,ECDHE-RSA-AES128-SHA256,ECDH-ECDSA-AES128-GCM-SHA256,ECDH-RSA-AES128-GCM-SHA256,ECDH-ECDSA-AES128-SHA256,ECDH-RSA-AES128-SHA256,DHE-DSS-AES128-GCM-SHA256,DHE-DSS-AES128-SHA256,AES128-GCM-SHA256,AES128-SHA256,ECDHE-ECDSA-AES256-SHA,ECDHE-RSA-AES256-SHA,DHE-DSS-AES256-SHA,ECDH-ECDSA-AES256-SHA,ECDH-RSA-AES256-SHA,AES256-SHA,ECDHE-ECDSA-AES128-SHA,ECDHE-RSA-AES128-SHA,DHE-DSS-AES128-SHA,ECDH-ECDSA-AES128-SHA,ECDH-RSA-AES128-SHA,AES128-SHA"
|
|
||||||
## fail_if_no_peer_cert = true
|
|
||||||
## inet6 = false
|
|
||||||
## ipv6_v6only = false
|
|
||||||
## }
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -14,50 +14,6 @@
|
||||||
%% limitations under the License.
|
%% limitations under the License.
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
%% Return Codes
|
|
||||||
-define(SUCCESS, 0). %% Success
|
|
||||||
-define(ERROR1, 101). %% badrpc
|
|
||||||
-define(ERROR2, 102). %% Unknown error
|
|
||||||
-define(ERROR3, 103). %% Username or password error
|
|
||||||
-define(ERROR4, 104). %% Empty username or password
|
|
||||||
-define(ERROR5, 105). %% User does not exist
|
|
||||||
-define(ERROR6, 106). %% Admin can not be deleted
|
|
||||||
-define(ERROR7, 107). %% Missing request parameter
|
|
||||||
-define(ERROR8, 108). %% Request parameter type error
|
|
||||||
-define(ERROR9, 109). %% Request parameter is not a json
|
|
||||||
-define(ERROR10, 110). %% Plugin has been loaded
|
|
||||||
-define(ERROR11, 111). %% Plugin has been unloaded
|
|
||||||
-define(ERROR12, 112). %% Client not online
|
|
||||||
-define(ERROR13, 113). %% User already exist
|
|
||||||
-define(ERROR14, 114). %% OldPassword error
|
|
||||||
-define(ERROR15, 115). %% bad topic
|
|
||||||
|
|
||||||
-define(VERSIONS, ["4.0", "4.1", "4.2", "4.3"]).
|
|
||||||
|
|
||||||
-define(MANAGEMENT_SHARD, emqx_management_shard).
|
-define(MANAGEMENT_SHARD, emqx_management_shard).
|
||||||
|
|
||||||
-define(GENERATE_API_METADATA(MetaData),
|
-define(MAX_ROW_LIMIT, 100).
|
||||||
maps:fold(
|
|
||||||
fun(Method, MethodDef0, NextMetaData) ->
|
|
||||||
Default = #{
|
|
||||||
tags => [?MODULE],
|
|
||||||
security => [#{application => []}]},
|
|
||||||
MethodDef =
|
|
||||||
lists:foldl(
|
|
||||||
fun(Key, NMethodDef) ->
|
|
||||||
case maps:is_key(Key, NMethodDef) of
|
|
||||||
true ->
|
|
||||||
NMethodDef;
|
|
||||||
false ->
|
|
||||||
maps:put(Key, maps:get(Key, Default), NMethodDef)
|
|
||||||
end
|
|
||||||
end, MethodDef0, maps:keys(Default)),
|
|
||||||
maps:put(Method, MethodDef, NextMetaData)
|
|
||||||
end,
|
|
||||||
#{}, MetaData)).
|
|
||||||
|
|
||||||
-define(GENERATE_API(Path, MetaData, Function),
|
|
||||||
{Path, ?GENERATE_API_METADATA(MetaData), Function}).
|
|
||||||
|
|
||||||
-define(GENERATE_APIS(Apis),
|
|
||||||
[?GENERATE_API(Path, MetaData, Function) || {Path, MetaData, Function} <- Apis]).
|
|
||||||
|
|
|
@ -22,36 +22,6 @@
|
||||||
-export([ structs/0
|
-export([ structs/0
|
||||||
, fields/1]).
|
, fields/1]).
|
||||||
|
|
||||||
structs() -> ["emqx_management"].
|
structs() -> [].
|
||||||
|
|
||||||
fields("emqx_management") ->
|
fields(_) -> [].
|
||||||
[ {applications, hoconsc:array(hoconsc:ref(?MODULE, "application"))}
|
|
||||||
, {max_row_limit, fun max_row_limit/1}
|
|
||||||
, {listeners, hoconsc:array(hoconsc:union([hoconsc:ref(?MODULE, "http"), hoconsc:ref(?MODULE, "https")]))}
|
|
||||||
];
|
|
||||||
|
|
||||||
fields("application") ->
|
|
||||||
[ {"id", emqx_schema:t(string(), undefined, "admin")}
|
|
||||||
, {"secret", emqx_schema:t(string(), undefined, "public")}
|
|
||||||
];
|
|
||||||
|
|
||||||
|
|
||||||
fields("http") ->
|
|
||||||
[ {"protocol", hoconsc:enum([http, https])}
|
|
||||||
, {"port", emqx_schema:t(integer(), undefined, 8081)}
|
|
||||||
, {"num_acceptors", emqx_schema:t(integer(), undefined, 4)}
|
|
||||||
, {"max_connections", emqx_schema:t(integer(), undefined, 512)}
|
|
||||||
, {"backlog", emqx_schema:t(integer(), undefined, 1024)}
|
|
||||||
, {"send_timeout", emqx_schema:t(emqx_schema:duration(), undefined, "15s")}
|
|
||||||
, {"send_timeout_close", emqx_schema:t(boolean(), undefined, true)}
|
|
||||||
, {"inet6", emqx_schema:t(boolean(), undefined, false)}
|
|
||||||
, {"ipv6_v6only", emqx_schema:t(boolean(), undefined, false)}
|
|
||||||
];
|
|
||||||
|
|
||||||
fields("https") ->
|
|
||||||
emqx_schema:ssl(#{enable => true}) ++ fields("http").
|
|
||||||
|
|
||||||
max_row_limit(type) -> integer();
|
|
||||||
max_row_limit(default) -> 1000;
|
|
||||||
max_row_limit(nullable) -> false;
|
|
||||||
max_row_limit(_) -> undefined.
|
|
||||||
|
|
|
@ -114,8 +114,6 @@
|
||||||
-export([ return/0
|
-export([ return/0
|
||||||
, return/1]).
|
, return/1]).
|
||||||
|
|
||||||
-define(MAX_ROW_LIMIT, 10000).
|
|
||||||
|
|
||||||
-define(APP, emqx_management).
|
-define(APP, emqx_management).
|
||||||
|
|
||||||
%% TODO: remove these function after all api use minirest version 1.X
|
%% TODO: remove these function after all api use minirest version 1.X
|
||||||
|
@ -590,7 +588,7 @@ check_row_limit([Tab|Tables], Limit) ->
|
||||||
end.
|
end.
|
||||||
|
|
||||||
max_row_limit() ->
|
max_row_limit() ->
|
||||||
emqx:get_config([?APP, max_row_limit], ?MAX_ROW_LIMIT).
|
?MAX_ROW_LIMIT.
|
||||||
|
|
||||||
table_size(Tab) -> ets:info(Tab, size).
|
table_size(Tab) -> ets:info(Tab, size).
|
||||||
|
|
||||||
|
|
|
@ -1,214 +0,0 @@
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved.
|
|
||||||
%%
|
|
||||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
%% you may not use this file except in compliance with the License.
|
|
||||||
%% You may obtain a copy of the License at
|
|
||||||
%%
|
|
||||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
%%
|
|
||||||
%% Unless required by applicable law or agreed to in writing, software
|
|
||||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
%% See the License for the specific language governing permissions and
|
|
||||||
%% limitations under the License.
|
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
|
|
||||||
-module(emqx_mgmt_api_apps).
|
|
||||||
|
|
||||||
-behaviour(minirest_api).
|
|
||||||
|
|
||||||
-import(emqx_mgmt_util, [ schema/1
|
|
||||||
, schema/2
|
|
||||||
, object_schema/1
|
|
||||||
, object_schema/2
|
|
||||||
, object_array_schema/2
|
|
||||||
, error_schema/1
|
|
||||||
, error_schema/2
|
|
||||||
, properties/1
|
|
||||||
]).
|
|
||||||
|
|
||||||
-export([api_spec/0]).
|
|
||||||
|
|
||||||
-export([ apps/2
|
|
||||||
, app/2]).
|
|
||||||
|
|
||||||
-define(BAD_APP_ID, 'BAD_APP_ID').
|
|
||||||
-define(APP_ID_NOT_FOUND, <<"{\"code\": \"BAD_APP_ID\", \"reason\": \"App id not found\"}">>).
|
|
||||||
|
|
||||||
api_spec() ->
|
|
||||||
{
|
|
||||||
[apps_api(), app_api()],
|
|
||||||
[]
|
|
||||||
}.
|
|
||||||
|
|
||||||
properties() ->
|
|
||||||
properties([
|
|
||||||
{app_id, string, <<"App ID">>},
|
|
||||||
{secret, string, <<"App Secret">>},
|
|
||||||
{name, string, <<"Dsiplay name">>},
|
|
||||||
{desc, string, <<"App description">>},
|
|
||||||
{status, boolean, <<"Enable or disable">>},
|
|
||||||
{expired, integer, <<"Expired time">>}
|
|
||||||
]).
|
|
||||||
|
|
||||||
%% not export schema
|
|
||||||
app_without_secret_schema() ->
|
|
||||||
maps:without([secret], properties()).
|
|
||||||
|
|
||||||
apps_api() ->
|
|
||||||
Metadata = #{
|
|
||||||
get => #{
|
|
||||||
description => <<"List EMQ X apps">>,
|
|
||||||
responses => #{
|
|
||||||
<<"200">> =>
|
|
||||||
object_array_schema(app_without_secret_schema(), <<"All apps">>)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
post => #{
|
|
||||||
description => <<"EMQ X create apps">>,
|
|
||||||
'requestBody' => schema(app),
|
|
||||||
responses => #{
|
|
||||||
<<"200">> =>
|
|
||||||
schema(app_secret, <<"Create apps">>),
|
|
||||||
<<"400">> =>
|
|
||||||
error_schema(<<"App ID already exist">>, [?BAD_APP_ID])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{"/apps", Metadata, apps}.
|
|
||||||
|
|
||||||
app_api() ->
|
|
||||||
Metadata = #{
|
|
||||||
get => #{
|
|
||||||
description => <<"EMQ X apps">>,
|
|
||||||
parameters => [#{
|
|
||||||
name => app_id,
|
|
||||||
in => path,
|
|
||||||
required => true,
|
|
||||||
schema => #{type => string}}],
|
|
||||||
responses => #{
|
|
||||||
<<"404">> =>
|
|
||||||
error_schema(<<"App id not found">>),
|
|
||||||
<<"200">> =>
|
|
||||||
object_schema(app_without_secret_schema(), <<"Get App">>)}},
|
|
||||||
delete => #{
|
|
||||||
description => <<"EMQ X apps">>,
|
|
||||||
parameters => [#{
|
|
||||||
name => app_id,
|
|
||||||
in => path,
|
|
||||||
required => true,
|
|
||||||
schema => #{type => string}
|
|
||||||
}],
|
|
||||||
responses => #{
|
|
||||||
<<"200">> => schema(<<"Remove app ok">>)}},
|
|
||||||
put => #{
|
|
||||||
description => <<"EMQ X update apps">>,
|
|
||||||
parameters => [#{
|
|
||||||
name => app_id,
|
|
||||||
in => path,
|
|
||||||
required => true,
|
|
||||||
schema => #{type => string}
|
|
||||||
}],
|
|
||||||
'requestBody' => object_schema(app_without_secret_schema()),
|
|
||||||
responses => #{
|
|
||||||
<<"404">> =>
|
|
||||||
error_schema(<<"App id not found">>, [?BAD_APP_ID]),
|
|
||||||
<<"200">> =>
|
|
||||||
object_schema(app_without_secret_schema(), <<"Update ok">>)}}},
|
|
||||||
{"/apps/:app_id", Metadata, app}.
|
|
||||||
|
|
||||||
%%%==============================================================================================
|
|
||||||
%% parameters trans
|
|
||||||
apps(get, _Params) ->
|
|
||||||
list(#{});
|
|
||||||
|
|
||||||
apps(post, #{body := Data}) ->
|
|
||||||
Parameters = #{
|
|
||||||
app_id => maps:get(<<"app_id">>, Data),
|
|
||||||
name => maps:get(<<"name">>, Data),
|
|
||||||
secret => maps:get(<<"secret">>, Data),
|
|
||||||
desc => maps:get(<<"desc">>, Data),
|
|
||||||
status => maps:get(<<"status">>, Data),
|
|
||||||
expired => maps:get(<<"expired">>, Data, undefined)
|
|
||||||
},
|
|
||||||
create(Parameters).
|
|
||||||
|
|
||||||
app(get, #{bindings := #{app_id := AppID}}) ->
|
|
||||||
lookup(#{app_id => AppID});
|
|
||||||
|
|
||||||
app(delete, #{bindings := #{app_id := AppID}}) ->
|
|
||||||
delete(#{app_id => AppID});
|
|
||||||
|
|
||||||
app(put, #{bindings := #{app_id := AppID}, body := Data}) ->
|
|
||||||
Parameters = #{
|
|
||||||
app_id => AppID,
|
|
||||||
name => maps:get(<<"name">>, Data),
|
|
||||||
desc => maps:get(<<"desc">>, Data),
|
|
||||||
status => maps:get(<<"status">>, Data),
|
|
||||||
expired => maps:get(<<"expired">>, Data, undefined)
|
|
||||||
},
|
|
||||||
update(Parameters).
|
|
||||||
|
|
||||||
|
|
||||||
%%%==============================================================================================
|
|
||||||
%% api apply
|
|
||||||
list(_) ->
|
|
||||||
{200, [format_without_app_secret(Apps) || Apps <- emqx_mgmt_auth:list_apps()]}.
|
|
||||||
|
|
||||||
create(#{app_id := AppID, name := Name, secret := Secret,
|
|
||||||
desc := Desc, status := Status, expired := Expired}) ->
|
|
||||||
case emqx_mgmt_auth:add_app(AppID, Name, Secret, Desc, Status, Expired) of
|
|
||||||
{ok, AppSecret} ->
|
|
||||||
{200, #{secret => AppSecret}};
|
|
||||||
{error, alread_existed} ->
|
|
||||||
Message = list_to_binary(io_lib:format("appid ~p already existed", [AppID])),
|
|
||||||
{400, #{code => 'BAD_APP_ID', message => Message}};
|
|
||||||
{error, Reason} ->
|
|
||||||
Response = #{code => 'UNKNOW_ERROR',
|
|
||||||
message => list_to_binary(io_lib:format("~p", [Reason]))},
|
|
||||||
{500, Response}
|
|
||||||
end.
|
|
||||||
|
|
||||||
lookup(#{app_id := AppID}) ->
|
|
||||||
case emqx_mgmt_auth:lookup_app(AppID) of
|
|
||||||
undefined ->
|
|
||||||
{404, ?APP_ID_NOT_FOUND};
|
|
||||||
App ->
|
|
||||||
Response = format_with_app_secret(App),
|
|
||||||
{200, Response}
|
|
||||||
end.
|
|
||||||
|
|
||||||
delete(#{app_id := AppID}) ->
|
|
||||||
_ = emqx_mgmt_auth:del_app(AppID),
|
|
||||||
{200}.
|
|
||||||
|
|
||||||
update(App = #{app_id := AppID, name := Name, desc := Desc, status := Status, expired := Expired}) ->
|
|
||||||
case emqx_mgmt_auth:update_app(AppID, Name, Desc, Status, Expired) of
|
|
||||||
ok ->
|
|
||||||
{200, App};
|
|
||||||
{error, not_found} ->
|
|
||||||
{404, ?APP_ID_NOT_FOUND};
|
|
||||||
{error, Reason} ->
|
|
||||||
Response = #{code => 'UNKNOW_ERROR', message => list_to_binary(io_lib:format("~p", [Reason]))},
|
|
||||||
{500, Response}
|
|
||||||
end.
|
|
||||||
|
|
||||||
%%%==============================================================================================
|
|
||||||
%% format
|
|
||||||
format_without_app_secret(App) ->
|
|
||||||
format_without([secret], App).
|
|
||||||
|
|
||||||
format_with_app_secret(App) ->
|
|
||||||
format_without([], App).
|
|
||||||
|
|
||||||
format_without(List, {AppID, AppSecret, Name, Desc, Status, Expired}) ->
|
|
||||||
Data = #{
|
|
||||||
app_id => AppID,
|
|
||||||
secret => AppSecret,
|
|
||||||
name => Name,
|
|
||||||
desc => Desc,
|
|
||||||
status => Status,
|
|
||||||
expired => Expired
|
|
||||||
},
|
|
||||||
maps:without(List, Data).
|
|
|
@ -1,47 +0,0 @@
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
%% Copyright (c) 2021 EMQ Technologies Co., Ltd. All Rights Reserved.
|
|
||||||
%%
|
|
||||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
%% you may not use this file except in compliance with the License.
|
|
||||||
%% You may obtain a copy of the License at
|
|
||||||
%%
|
|
||||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
%%
|
|
||||||
%% Unless required by applicable law or agreed to in writing, software
|
|
||||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
%% See the License for the specific language governing permissions and
|
|
||||||
%% limitations under the License.
|
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
|
|
||||||
-module(emqx_mgmt_api_authz).
|
|
||||||
|
|
||||||
-include("emqx_mgmt.hrl").
|
|
||||||
|
|
||||||
-rest_api(#{name => clean_authz_cache_all,
|
|
||||||
method => 'DELETE',
|
|
||||||
path => "/authz-cache",
|
|
||||||
func => clean_all,
|
|
||||||
descr => "Clean authz cache on all nodes"}).
|
|
||||||
|
|
||||||
-rest_api(#{name => clean_authz_cache_node,
|
|
||||||
method => 'DELETE',
|
|
||||||
path => "nodes/:atom:node/authz-cache",
|
|
||||||
func => clean_node,
|
|
||||||
descr => "Clean authz cache on specific node"}).
|
|
||||||
|
|
||||||
-export([ clean_all/2
|
|
||||||
, clean_node/2
|
|
||||||
]).
|
|
||||||
|
|
||||||
clean_all(_Bindings, _Params) ->
|
|
||||||
case emqx_mgmt:clean_authz_cache_all() of
|
|
||||||
ok -> emqx_mgmt:return();
|
|
||||||
{error, Reason} -> emqx_mgmt:return({error, ?ERROR1, Reason})
|
|
||||||
end.
|
|
||||||
|
|
||||||
clean_node(#{node := Node}, _Params) ->
|
|
||||||
case emqx_mgmt:clean_authz_cache_all(Node) of
|
|
||||||
ok -> emqx_mgmt:return();
|
|
||||||
{error, Reason} -> emqx_mgmt:return({error, ?ERROR1, Reason})
|
|
||||||
end.
|
|
|
@ -1,166 +0,0 @@
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved.
|
|
||||||
%%
|
|
||||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
%% you may not use this file except in compliance with the License.
|
|
||||||
%% You may obtain a copy of the License at
|
|
||||||
%%
|
|
||||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
%%
|
|
||||||
%% Unless required by applicable law or agreed to in writing, software
|
|
||||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
%% See the License for the specific language governing permissions and
|
|
||||||
%% limitations under the License.
|
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
|
|
||||||
-module(emqx_mgmt_api_banned).
|
|
||||||
|
|
||||||
-include_lib("emqx/include/emqx.hrl").
|
|
||||||
|
|
||||||
-include("emqx_mgmt.hrl").
|
|
||||||
|
|
||||||
-rest_api(#{name => list_banned,
|
|
||||||
method => 'GET',
|
|
||||||
path => "/banned/",
|
|
||||||
func => list,
|
|
||||||
descr => "List banned"}).
|
|
||||||
|
|
||||||
-rest_api(#{name => create_banned,
|
|
||||||
method => 'POST',
|
|
||||||
path => "/banned/",
|
|
||||||
func => create,
|
|
||||||
descr => "Create banned"}).
|
|
||||||
|
|
||||||
-rest_api(#{name => delete_banned,
|
|
||||||
method => 'DELETE',
|
|
||||||
path => "/banned/:as/:who",
|
|
||||||
func => delete,
|
|
||||||
descr => "Delete banned"}).
|
|
||||||
|
|
||||||
-export([ list/2
|
|
||||||
, create/2
|
|
||||||
, delete/2
|
|
||||||
]).
|
|
||||||
|
|
||||||
list(_Bindings, Params) ->
|
|
||||||
emqx_mgmt:return({ok, emqx_mgmt_api:paginate(emqx_banned, Params, fun format/1)}).
|
|
||||||
|
|
||||||
create(_Bindings, Params) ->
|
|
||||||
case pipeline([fun ensure_required/1,
|
|
||||||
fun validate_params/1], Params) of
|
|
||||||
{ok, NParams} ->
|
|
||||||
{ok, Banned} = pack_banned(NParams),
|
|
||||||
ok = emqx_mgmt:create_banned(Banned),
|
|
||||||
emqx_mgmt:return({ok, maps:from_list(Params)});
|
|
||||||
{error, Code, Message} ->
|
|
||||||
emqx_mgmt:return({error, Code, Message})
|
|
||||||
end.
|
|
||||||
|
|
||||||
delete(#{as := As, who := Who}, _) ->
|
|
||||||
Params = [{<<"who">>, bin(emqx_mgmt_util:urldecode(Who))},
|
|
||||||
{<<"as">>, bin(emqx_mgmt_util:urldecode(As))}],
|
|
||||||
case pipeline([fun ensure_required/1,
|
|
||||||
fun validate_params/1], Params) of
|
|
||||||
{ok, NParams} ->
|
|
||||||
do_delete(proplists:get_value(<<"as">>, NParams), proplists:get_value(<<"who">>, NParams)),
|
|
||||||
emqx_mgmt:return();
|
|
||||||
{error, Code, Message} ->
|
|
||||||
emqx_mgmt:return({error, Code, Message})
|
|
||||||
end.
|
|
||||||
|
|
||||||
pipeline([], Params) ->
|
|
||||||
{ok, Params};
|
|
||||||
pipeline([Fun|More], Params) ->
|
|
||||||
case Fun(Params) of
|
|
||||||
{ok, NParams} ->
|
|
||||||
pipeline(More, NParams);
|
|
||||||
{error, Code, Message} ->
|
|
||||||
{error, Code, Message}
|
|
||||||
end.
|
|
||||||
|
|
||||||
%% Plugs
|
|
||||||
ensure_required(Params) when is_list(Params) ->
|
|
||||||
#{required_params := RequiredParams, message := Msg} = required_params(),
|
|
||||||
AllIncluded = lists:all(fun(Key) ->
|
|
||||||
lists:keymember(Key, 1, Params)
|
|
||||||
end, RequiredParams),
|
|
||||||
case AllIncluded of
|
|
||||||
true -> {ok, Params};
|
|
||||||
false ->
|
|
||||||
{error, ?ERROR7, Msg}
|
|
||||||
end.
|
|
||||||
|
|
||||||
validate_params(Params) ->
|
|
||||||
#{enum_values := AsEnums, message := Msg} = enum_values(as),
|
|
||||||
case lists:member(proplists:get_value(<<"as">>, Params), AsEnums) of
|
|
||||||
true -> {ok, Params};
|
|
||||||
false ->
|
|
||||||
{error, ?ERROR8, Msg}
|
|
||||||
end.
|
|
||||||
|
|
||||||
pack_banned(Params) ->
|
|
||||||
Now = erlang:system_time(second),
|
|
||||||
do_pack_banned(Params, #{by => <<"user">>, at => Now, until => Now + 300}).
|
|
||||||
|
|
||||||
do_pack_banned([], #{who := Who, by := By, reason := Reason, at := At, until := Until}) ->
|
|
||||||
{ok, #banned{who = Who, by = By, reason = Reason, at = At, until = Until}};
|
|
||||||
do_pack_banned([{<<"who">>, Who} | Params], Banned) ->
|
|
||||||
case lists:keytake(<<"as">>, 1, Params) of
|
|
||||||
{value, {<<"as">>, <<"peerhost">>}, Params2} ->
|
|
||||||
{ok, IPAddress} = inet:parse_address(str(Who)),
|
|
||||||
do_pack_banned(Params2, Banned#{who => {peerhost, IPAddress}});
|
|
||||||
{value, {<<"as">>, <<"clientid">>}, Params2} ->
|
|
||||||
do_pack_banned(Params2, Banned#{who => {clientid, Who}});
|
|
||||||
{value, {<<"as">>, <<"username">>}, Params2} ->
|
|
||||||
do_pack_banned(Params2, Banned#{who => {username, Who}})
|
|
||||||
end;
|
|
||||||
do_pack_banned([P1 = {<<"as">>, _}, P2 | Params], Banned) ->
|
|
||||||
do_pack_banned([P2, P1 | Params], Banned);
|
|
||||||
do_pack_banned([{<<"by">>, By} | Params], Banned) ->
|
|
||||||
do_pack_banned(Params, Banned#{by => By});
|
|
||||||
do_pack_banned([{<<"reason">>, Reason} | Params], Banned) ->
|
|
||||||
do_pack_banned(Params, Banned#{reason => Reason});
|
|
||||||
do_pack_banned([{<<"at">>, At} | Params], Banned) ->
|
|
||||||
do_pack_banned(Params, Banned#{at => At});
|
|
||||||
do_pack_banned([{<<"until">>, Until} | Params], Banned) ->
|
|
||||||
do_pack_banned(Params, Banned#{until => Until});
|
|
||||||
do_pack_banned([_P | Params], Banned) -> %% ignore other params
|
|
||||||
do_pack_banned(Params, Banned).
|
|
||||||
|
|
||||||
do_delete(<<"peerhost">>, Who) ->
|
|
||||||
{ok, IPAddress} = inet:parse_address(str(Who)),
|
|
||||||
emqx_mgmt:delete_banned({peerhost, IPAddress});
|
|
||||||
do_delete(<<"username">>, Who) ->
|
|
||||||
emqx_mgmt:delete_banned({username, bin(Who)});
|
|
||||||
do_delete(<<"clientid">>, Who) ->
|
|
||||||
emqx_mgmt:delete_banned({clientid, bin(Who)}).
|
|
||||||
|
|
||||||
required_params() ->
|
|
||||||
#{required_params => [<<"who">>, <<"as">>],
|
|
||||||
message => <<"missing mandatory params: ['who', 'as']">> }.
|
|
||||||
|
|
||||||
enum_values(as) ->
|
|
||||||
#{enum_values => [<<"clientid">>, <<"username">>, <<"peerhost">>],
|
|
||||||
message => <<"value of 'as' must be one of: ['clientid', 'username', 'peerhost']">> }.
|
|
||||||
|
|
||||||
%% Internal Functions
|
|
||||||
|
|
||||||
format(BannedList) when is_list(BannedList) ->
|
|
||||||
[format(Ban) || Ban <- BannedList];
|
|
||||||
format(#banned{who = {As, Who}, by = By, reason = Reason, at = At, until = Until}) ->
|
|
||||||
#{who => case As of
|
|
||||||
peerhost -> bin(inet:ntoa(Who));
|
|
||||||
_ -> Who
|
|
||||||
end,
|
|
||||||
as => As, by => By, reason => Reason, at => At, until => Until}.
|
|
||||||
|
|
||||||
bin(L) when is_list(L) ->
|
|
||||||
list_to_binary(L);
|
|
||||||
bin(B) when is_binary(B) ->
|
|
||||||
B.
|
|
||||||
|
|
||||||
str(B) when is_binary(B) ->
|
|
||||||
binary_to_list(B);
|
|
||||||
str(L) when is_list(L) ->
|
|
||||||
L.
|
|
|
@ -1,47 +0,0 @@
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved.
|
|
||||||
%%
|
|
||||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
%% you may not use this file except in compliance with the License.
|
|
||||||
%% You may obtain a copy of the License at
|
|
||||||
%%
|
|
||||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
%%
|
|
||||||
%% Unless required by applicable law or agreed to in writing, software
|
|
||||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
%% See the License for the specific language governing permissions and
|
|
||||||
%% limitations under the License.
|
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
|
|
||||||
-module(emqx_mgmt_api_brokers).
|
|
||||||
|
|
||||||
-include("emqx_mgmt.hrl").
|
|
||||||
|
|
||||||
-rest_api(#{name => list_brokers,
|
|
||||||
method => 'GET',
|
|
||||||
path => "/brokers/",
|
|
||||||
func => list,
|
|
||||||
descr => "A list of brokers in the cluster"}).
|
|
||||||
|
|
||||||
-rest_api(#{name => get_broker,
|
|
||||||
method => 'GET',
|
|
||||||
path => "/brokers/:atom:node",
|
|
||||||
func => get,
|
|
||||||
descr => "Get broker info of a node"}).
|
|
||||||
|
|
||||||
-export([ list/2
|
|
||||||
, get/2
|
|
||||||
]).
|
|
||||||
|
|
||||||
list(_Bindings, _Params) ->
|
|
||||||
emqx_mgmt:return({ok, [Info || {_Node, Info} <- emqx_mgmt:list_brokers()]}).
|
|
||||||
|
|
||||||
get(#{node := Node}, _Params) ->
|
|
||||||
case emqx_mgmt:lookup_broker(Node) of
|
|
||||||
{error, Reason} ->
|
|
||||||
emqx_mgmt:return({error, ?ERROR2, Reason});
|
|
||||||
Info ->
|
|
||||||
emqx_mgmt:return({ok, Info})
|
|
||||||
end.
|
|
||||||
|
|
|
@ -1,113 +0,0 @@
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved.
|
|
||||||
%%
|
|
||||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
%% you may not use this file except in compliance with the License.
|
|
||||||
%% You may obtain a copy of the License at
|
|
||||||
%%
|
|
||||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
%%
|
|
||||||
%% Unless required by applicable law or agreed to in writing, software
|
|
||||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
%% See the License for the specific language governing permissions and
|
|
||||||
%% limitations under the License.
|
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
|
|
||||||
-module(emqx_mgmt_api_plugins).
|
|
||||||
|
|
||||||
-include("emqx_mgmt.hrl").
|
|
||||||
|
|
||||||
-include_lib("emqx/include/emqx.hrl").
|
|
||||||
|
|
||||||
-rest_api(#{name => list_all_plugins,
|
|
||||||
method => 'GET',
|
|
||||||
path => "/plugins/",
|
|
||||||
func => list,
|
|
||||||
descr => "List all plugins in the cluster"}).
|
|
||||||
|
|
||||||
-rest_api(#{name => list_node_plugins,
|
|
||||||
method => 'GET',
|
|
||||||
path => "/nodes/:atom:node/plugins/",
|
|
||||||
func => list,
|
|
||||||
descr => "List all plugins on a node"}).
|
|
||||||
|
|
||||||
-rest_api(#{name => load_node_plugin,
|
|
||||||
method => 'PUT',
|
|
||||||
path => "/nodes/:atom:node/plugins/:atom:plugin/load",
|
|
||||||
func => load,
|
|
||||||
descr => "Load a plugin"}).
|
|
||||||
|
|
||||||
-rest_api(#{name => unload_node_plugin,
|
|
||||||
method => 'PUT',
|
|
||||||
path => "/nodes/:atom:node/plugins/:atom:plugin/unload",
|
|
||||||
func => unload,
|
|
||||||
descr => "Unload a plugin"}).
|
|
||||||
|
|
||||||
-rest_api(#{name => reload_node_plugin,
|
|
||||||
method => 'PUT',
|
|
||||||
path => "/nodes/:atom:node/plugins/:atom:plugin/reload",
|
|
||||||
func => reload,
|
|
||||||
descr => "Reload a plugin"}).
|
|
||||||
|
|
||||||
-rest_api(#{name => unload_plugin,
|
|
||||||
method => 'PUT',
|
|
||||||
path => "/plugins/:atom:plugin/unload",
|
|
||||||
func => unload,
|
|
||||||
descr => "Unload a plugin in the cluster"}).
|
|
||||||
|
|
||||||
-rest_api(#{name => reload_plugin,
|
|
||||||
method => 'PUT',
|
|
||||||
path => "/plugins/:atom:plugin/reload",
|
|
||||||
func => reload,
|
|
||||||
descr => "Reload a plugin in the cluster"}).
|
|
||||||
|
|
||||||
-export([ list/2
|
|
||||||
, load/2
|
|
||||||
, unload/2
|
|
||||||
, reload/2
|
|
||||||
]).
|
|
||||||
|
|
||||||
list(#{node := Node}, _Params) ->
|
|
||||||
emqx_mgmt:return({ok, [format(Plugin) || Plugin <- emqx_mgmt:list_plugins(Node)]});
|
|
||||||
|
|
||||||
list(_Bindings, _Params) ->
|
|
||||||
emqx_mgmt:return({ok, [format({Node, Plugins}) || {Node, Plugins} <- emqx_mgmt:list_plugins()]}).
|
|
||||||
|
|
||||||
load(#{node := Node, plugin := Plugin}, _Params) ->
|
|
||||||
emqx_mgmt:return(emqx_mgmt:load_plugin(Node, Plugin)).
|
|
||||||
|
|
||||||
unload(#{node := Node, plugin := Plugin}, _Params) ->
|
|
||||||
emqx_mgmt:return(emqx_mgmt:unload_plugin(Node, Plugin));
|
|
||||||
|
|
||||||
unload(#{plugin := Plugin}, _Params) ->
|
|
||||||
Results = [emqx_mgmt:unload_plugin(Node, Plugin) || {Node, _Info} <- emqx_mgmt:list_nodes()],
|
|
||||||
case lists:filter(fun(Item) -> Item =/= ok end, Results) of
|
|
||||||
[] ->
|
|
||||||
emqx_mgmt:return(ok);
|
|
||||||
Errors ->
|
|
||||||
emqx_mgmt:return(lists:last(Errors))
|
|
||||||
end.
|
|
||||||
|
|
||||||
reload(#{node := Node, plugin := Plugin}, _Params) ->
|
|
||||||
emqx_mgmt:return(emqx_mgmt:reload_plugin(Node, Plugin));
|
|
||||||
|
|
||||||
reload(#{plugin := Plugin}, _Params) ->
|
|
||||||
Results = [emqx_mgmt:reload_plugin(Node, Plugin) || {Node, _Info} <- emqx_mgmt:list_nodes()],
|
|
||||||
case lists:filter(fun(Item) -> Item =/= ok end, Results) of
|
|
||||||
[] ->
|
|
||||||
emqx_mgmt:return(ok);
|
|
||||||
Errors ->
|
|
||||||
emqx_mgmt:return(lists:last(Errors))
|
|
||||||
end.
|
|
||||||
|
|
||||||
format({Node, Plugins}) ->
|
|
||||||
#{node => Node, plugins => [format(Plugin) || Plugin <- Plugins]};
|
|
||||||
|
|
||||||
format(#plugin{name = Name,
|
|
||||||
descr = Descr,
|
|
||||||
active = Active}) ->
|
|
||||||
#{name => Name,
|
|
||||||
description => iolist_to_binary(Descr),
|
|
||||||
active => Active}.
|
|
||||||
|
|
|
@ -1,247 +0,0 @@
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved.
|
|
||||||
%%
|
|
||||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
%% you may not use this file except in compliance with the License.
|
|
||||||
%% You may obtain a copy of the License at
|
|
||||||
%%
|
|
||||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
%%
|
|
||||||
%% Unless required by applicable law or agreed to in writing, software
|
|
||||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
%% See the License for the specific language governing permissions and
|
|
||||||
%% limitations under the License.
|
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
|
|
||||||
-module(emqx_mgmt_api_pubsub).
|
|
||||||
|
|
||||||
-include_lib("emqx/include/emqx.hrl").
|
|
||||||
-include_lib("emqx/include/emqx_mqtt.hrl").
|
|
||||||
-include("emqx_mgmt.hrl").
|
|
||||||
|
|
||||||
-rest_api(#{name => mqtt_subscribe,
|
|
||||||
method => 'POST',
|
|
||||||
path => "/mqtt/subscribe",
|
|
||||||
func => subscribe,
|
|
||||||
descr => "Subscribe a topic"}).
|
|
||||||
|
|
||||||
-rest_api(#{name => mqtt_publish,
|
|
||||||
method => 'POST',
|
|
||||||
path => "/mqtt/publish",
|
|
||||||
func => publish,
|
|
||||||
descr => "Publish a MQTT message"}).
|
|
||||||
|
|
||||||
-rest_api(#{name => mqtt_unsubscribe,
|
|
||||||
method => 'POST',
|
|
||||||
path => "/mqtt/unsubscribe",
|
|
||||||
func => unsubscribe,
|
|
||||||
descr => "Unsubscribe a topic"}).
|
|
||||||
|
|
||||||
-rest_api(#{name => mqtt_subscribe_batch,
|
|
||||||
method => 'POST',
|
|
||||||
path => "/mqtt/subscribe_batch",
|
|
||||||
func => subscribe_batch,
|
|
||||||
descr => "Batch subscribes topics"}).
|
|
||||||
|
|
||||||
-rest_api(#{name => mqtt_publish_batch,
|
|
||||||
method => 'POST',
|
|
||||||
path => "/mqtt/publish_batch",
|
|
||||||
func => publish_batch,
|
|
||||||
descr => "Batch publish MQTT messages"}).
|
|
||||||
|
|
||||||
-rest_api(#{name => mqtt_unsubscribe_batch,
|
|
||||||
method => 'POST',
|
|
||||||
path => "/mqtt/unsubscribe_batch",
|
|
||||||
func => unsubscribe_batch,
|
|
||||||
descr => "Batch unsubscribes topics"}).
|
|
||||||
|
|
||||||
-export([ subscribe/2
|
|
||||||
, publish/2
|
|
||||||
, unsubscribe/2
|
|
||||||
, subscribe_batch/2
|
|
||||||
, publish_batch/2
|
|
||||||
, unsubscribe_batch/2
|
|
||||||
]).
|
|
||||||
|
|
||||||
subscribe(_Bindings, Params) ->
|
|
||||||
logger:debug("API subscribe Params:~p", [Params]),
|
|
||||||
{ClientId, Topic, QoS} = parse_subscribe_params(Params),
|
|
||||||
emqx_mgmt:return(do_subscribe(ClientId, Topic, QoS)).
|
|
||||||
|
|
||||||
publish(_Bindings, Params) ->
|
|
||||||
logger:debug("API publish Params:~p", [Params]),
|
|
||||||
{ClientId, Topic, Qos, Retain, Payload} = parse_publish_params(Params),
|
|
||||||
case do_publish(ClientId, Topic, Qos, Retain, Payload) of
|
|
||||||
{ok, MsgIds} ->
|
|
||||||
case proplists:get_value(<<"return">>, Params, undefined) of
|
|
||||||
undefined -> emqx_mgmt:return(ok);
|
|
||||||
_Val ->
|
|
||||||
case proplists:get_value(<<"topics">>, Params, undefined) of
|
|
||||||
undefined -> emqx_mgmt:return({ok, #{msgid => lists:last(MsgIds)}});
|
|
||||||
_ -> emqx_mgmt:return({ok, #{msgids => MsgIds}})
|
|
||||||
end
|
|
||||||
end;
|
|
||||||
Result ->
|
|
||||||
emqx_mgmt:return(Result)
|
|
||||||
end.
|
|
||||||
|
|
||||||
unsubscribe(_Bindings, Params) ->
|
|
||||||
logger:debug("API unsubscribe Params:~p", [Params]),
|
|
||||||
{ClientId, Topic} = parse_unsubscribe_params(Params),
|
|
||||||
emqx_mgmt:return(do_unsubscribe(ClientId, Topic)).
|
|
||||||
|
|
||||||
subscribe_batch(_Bindings, Params) ->
|
|
||||||
logger:debug("API subscribe batch Params:~p", [Params]),
|
|
||||||
emqx_mgmt:return({ok, loop_subscribe(Params)}).
|
|
||||||
|
|
||||||
publish_batch(_Bindings, Params) ->
|
|
||||||
logger:debug("API publish batch Params:~p", [Params]),
|
|
||||||
emqx_mgmt:return({ok, loop_publish(Params)}).
|
|
||||||
|
|
||||||
unsubscribe_batch(_Bindings, Params) ->
|
|
||||||
logger:debug("API unsubscribe batch Params:~p", [Params]),
|
|
||||||
emqx_mgmt:return({ok, loop_unsubscribe(Params)}).
|
|
||||||
|
|
||||||
loop_subscribe(Params) ->
|
|
||||||
loop_subscribe(Params, []).
|
|
||||||
loop_subscribe([], Result) ->
|
|
||||||
lists:reverse(Result);
|
|
||||||
loop_subscribe([Params | ParamsN], Acc) ->
|
|
||||||
{ClientId, Topic, QoS} = parse_subscribe_params(Params),
|
|
||||||
Code = case do_subscribe(ClientId, Topic, QoS) of
|
|
||||||
ok -> 0;
|
|
||||||
{_, Code0, _Reason} -> Code0
|
|
||||||
end,
|
|
||||||
Result = #{clientid => ClientId,
|
|
||||||
topic => resp_topic(proplists:get_value(<<"topic">>, Params), proplists:get_value(<<"topics">>, Params, <<"">>)),
|
|
||||||
code => Code},
|
|
||||||
loop_subscribe(ParamsN, [Result | Acc]).
|
|
||||||
|
|
||||||
loop_publish(Params) ->
|
|
||||||
loop_publish(Params, []).
|
|
||||||
loop_publish([], Result) ->
|
|
||||||
lists:reverse(Result);
|
|
||||||
loop_publish([Params | ParamsN], Acc) ->
|
|
||||||
{ClientId, Topic, Qos, Retain, Payload} = parse_publish_params(Params),
|
|
||||||
Code = case do_publish(ClientId, Topic, Qos, Retain, Payload) of
|
|
||||||
{ok, _} -> 0;
|
|
||||||
{_, Code0, _} -> Code0
|
|
||||||
end,
|
|
||||||
Result = #{topic => resp_topic(proplists:get_value(<<"topic">>, Params), proplists:get_value(<<"topics">>, Params, <<"">>)),
|
|
||||||
code => Code},
|
|
||||||
loop_publish(ParamsN, [Result | Acc]).
|
|
||||||
|
|
||||||
loop_unsubscribe(Params) ->
|
|
||||||
loop_unsubscribe(Params, []).
|
|
||||||
loop_unsubscribe([], Result) ->
|
|
||||||
lists:reverse(Result);
|
|
||||||
loop_unsubscribe([Params | ParamsN], Acc) ->
|
|
||||||
{ClientId, Topic} = parse_unsubscribe_params(Params),
|
|
||||||
Code = case do_unsubscribe(ClientId, Topic) of
|
|
||||||
ok -> 0;
|
|
||||||
{_, Code0, _} -> Code0
|
|
||||||
end,
|
|
||||||
Result = #{clientid => ClientId,
|
|
||||||
topic => resp_topic(proplists:get_value(<<"topic">>, Params), proplists:get_value(<<"topics">>, Params, <<"">>)),
|
|
||||||
code => Code},
|
|
||||||
loop_unsubscribe(ParamsN, [Result | Acc]).
|
|
||||||
|
|
||||||
do_subscribe(_ClientId, [], _QoS) ->
|
|
||||||
{ok, ?ERROR15, bad_topic};
|
|
||||||
do_subscribe(ClientId, Topics, QoS) ->
|
|
||||||
TopicTable = parse_topic_filters(Topics, QoS),
|
|
||||||
case emqx_mgmt:subscribe(ClientId, TopicTable) of
|
|
||||||
{error, Reason} -> {ok, ?ERROR12, Reason};
|
|
||||||
_ -> ok
|
|
||||||
end.
|
|
||||||
|
|
||||||
do_publish(_ClientId, [], _Qos, _Retain, _Payload) ->
|
|
||||||
{ok, ?ERROR15, bad_topic};
|
|
||||||
do_publish(ClientId, Topics, Qos, Retain, Payload) ->
|
|
||||||
MsgIds = lists:map(fun(Topic) ->
|
|
||||||
Msg = emqx_message:make(ClientId, Qos, Topic, Payload),
|
|
||||||
_ = emqx_mgmt:publish(Msg#message{flags = #{retain => Retain}}),
|
|
||||||
emqx_guid:to_hexstr(Msg#message.id)
|
|
||||||
end, Topics),
|
|
||||||
{ok, MsgIds}.
|
|
||||||
|
|
||||||
do_unsubscribe(ClientId, Topic) ->
|
|
||||||
case validate_by_filter(Topic) of
|
|
||||||
true ->
|
|
||||||
case emqx_mgmt:unsubscribe(ClientId, Topic) of
|
|
||||||
{error, Reason} -> {ok, ?ERROR12, Reason};
|
|
||||||
_ -> ok
|
|
||||||
end;
|
|
||||||
false ->
|
|
||||||
{ok, ?ERROR15, bad_topic}
|
|
||||||
end.
|
|
||||||
|
|
||||||
parse_subscribe_params(Params) ->
|
|
||||||
ClientId = proplists:get_value(<<"clientid">>, Params),
|
|
||||||
Topics = topics(filter, proplists:get_value(<<"topic">>, Params), proplists:get_value(<<"topics">>, Params, <<"">>)),
|
|
||||||
QoS = proplists:get_value(<<"qos">>, Params, 0),
|
|
||||||
{ClientId, Topics, QoS}.
|
|
||||||
|
|
||||||
parse_publish_params(Params) ->
|
|
||||||
Topics = topics(name, proplists:get_value(<<"topic">>, Params), proplists:get_value(<<"topics">>, Params, <<"">>)),
|
|
||||||
ClientId = proplists:get_value(<<"clientid">>, Params),
|
|
||||||
Payload = decode_payload(proplists:get_value(<<"payload">>, Params, <<>>),
|
|
||||||
proplists:get_value(<<"encoding">>, Params, <<"plain">>)),
|
|
||||||
Qos = proplists:get_value(<<"qos">>, Params, 0),
|
|
||||||
Retain = proplists:get_value(<<"retain">>, Params, false),
|
|
||||||
Payload1 = maybe_maps_to_binary(Payload),
|
|
||||||
{ClientId, Topics, Qos, Retain, Payload1}.
|
|
||||||
|
|
||||||
parse_unsubscribe_params(Params) ->
|
|
||||||
ClientId = proplists:get_value(<<"clientid">>, Params),
|
|
||||||
Topic = proplists:get_value(<<"topic">>, Params),
|
|
||||||
{ClientId, Topic}.
|
|
||||||
|
|
||||||
topics(Type, undefined, Topics0) ->
|
|
||||||
Topics = binary:split(Topics0, <<",">>, [global]),
|
|
||||||
case Type of
|
|
||||||
name -> lists:filter(fun(T) -> validate_by_name(T) end, Topics);
|
|
||||||
filter -> lists:filter(fun(T) -> validate_by_filter(T) end, Topics)
|
|
||||||
end;
|
|
||||||
|
|
||||||
topics(Type, Topic, _) ->
|
|
||||||
topics(Type, undefined, Topic).
|
|
||||||
|
|
||||||
%%TODO:
|
|
||||||
% validate(qos, Qos) ->
|
|
||||||
% (Qos >= ?QOS_0) and (Qos =< ?QOS_2).
|
|
||||||
|
|
||||||
validate_by_filter(Topic) ->
|
|
||||||
validate_topic({filter, Topic}).
|
|
||||||
|
|
||||||
validate_by_name(Topic) ->
|
|
||||||
validate_topic({name, Topic}).
|
|
||||||
|
|
||||||
validate_topic({Type, Topic}) ->
|
|
||||||
try emqx_topic:validate({Type, Topic}) of
|
|
||||||
true -> true
|
|
||||||
catch
|
|
||||||
error:_Reason -> false
|
|
||||||
end.
|
|
||||||
|
|
||||||
parse_topic_filters(Topics, Qos) ->
|
|
||||||
[begin
|
|
||||||
{Topic, Opts} = emqx_topic:parse(Topic0),
|
|
||||||
{Topic, Opts#{qos => Qos}}
|
|
||||||
end || Topic0 <- Topics].
|
|
||||||
|
|
||||||
resp_topic(undefined, Topics) -> Topics;
|
|
||||||
resp_topic(Topic, _) -> Topic.
|
|
||||||
|
|
||||||
decode_payload(Payload, <<"base64">>) -> base64:decode(Payload);
|
|
||||||
decode_payload(Payload, _) -> Payload.
|
|
||||||
|
|
||||||
maybe_maps_to_binary(Payload) when is_binary(Payload) -> Payload;
|
|
||||||
maybe_maps_to_binary(Payload) ->
|
|
||||||
try
|
|
||||||
emqx_json:encode(Payload)
|
|
||||||
catch
|
|
||||||
_C : _E : S ->
|
|
||||||
error({encode_payload_fail, S})
|
|
||||||
end.
|
|
|
@ -29,10 +29,8 @@
|
||||||
start(_Type, _Args) ->
|
start(_Type, _Args) ->
|
||||||
{ok, Sup} = emqx_mgmt_sup:start_link(),
|
{ok, Sup} = emqx_mgmt_sup:start_link(),
|
||||||
ok = ekka_rlog:wait_for_shards([?MANAGEMENT_SHARD], infinity),
|
ok = ekka_rlog:wait_for_shards([?MANAGEMENT_SHARD], infinity),
|
||||||
_ = emqx_mgmt_auth:add_default_app(),
|
|
||||||
emqx_mgmt_http:start_listeners(),
|
|
||||||
emqx_mgmt_cli:load(),
|
emqx_mgmt_cli:load(),
|
||||||
{ok, Sup}.
|
{ok, Sup}.
|
||||||
|
|
||||||
stop(_State) ->
|
stop(_State) ->
|
||||||
emqx_mgmt_http:stop_listeners().
|
ok.
|
||||||
|
|
|
@ -1,216 +0,0 @@
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved.
|
|
||||||
%%
|
|
||||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
%% you may not use this file except in compliance with the License.
|
|
||||||
%% You may obtain a copy of the License at
|
|
||||||
%%
|
|
||||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
%%
|
|
||||||
%% Unless required by applicable law or agreed to in writing, software
|
|
||||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
%% See the License for the specific language governing permissions and
|
|
||||||
%% limitations under the License.
|
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
|
|
||||||
-module(emqx_mgmt_auth).
|
|
||||||
|
|
||||||
%% Mnesia Bootstrap
|
|
||||||
-export([mnesia/1]).
|
|
||||||
-boot_mnesia({mnesia, [boot]}).
|
|
||||||
-copy_mnesia({mnesia, [copy]}).
|
|
||||||
|
|
||||||
%% APP Management API
|
|
||||||
-export([ add_default_app/0
|
|
||||||
, add_app/2
|
|
||||||
, add_app/5
|
|
||||||
, add_app/6
|
|
||||||
, force_add_app/6
|
|
||||||
, lookup_app/1
|
|
||||||
, get_appsecret/1
|
|
||||||
, update_app/2
|
|
||||||
, update_app/5
|
|
||||||
, del_app/1
|
|
||||||
, list_apps/0
|
|
||||||
]).
|
|
||||||
|
|
||||||
%% APP Auth/Authorization API
|
|
||||||
-export([is_authorized/2]).
|
|
||||||
|
|
||||||
-define(APP, emqx_management).
|
|
||||||
|
|
||||||
-record(mqtt_app, {id, secret, name, desc, status, expired}).
|
|
||||||
|
|
||||||
-type(appid() :: binary()).
|
|
||||||
|
|
||||||
-type(appsecret() :: binary()).
|
|
||||||
|
|
||||||
-include("emqx_mgmt.hrl").
|
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
%% Mnesia Bootstrap
|
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
|
|
||||||
mnesia(boot) ->
|
|
||||||
ok = ekka_mnesia:create_table(mqtt_app, [
|
|
||||||
{rlog_shard, ?MANAGEMENT_SHARD},
|
|
||||||
{disc_copies, [node()]},
|
|
||||||
{record_name, mqtt_app},
|
|
||||||
{attributes, record_info(fields, mqtt_app)}]);
|
|
||||||
|
|
||||||
mnesia(copy) ->
|
|
||||||
ok = ekka_mnesia:copy_table(mqtt_app, disc_copies).
|
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
%% Manage Apps
|
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
-spec(add_default_app() -> list()).
|
|
||||||
add_default_app() ->
|
|
||||||
Apps = emqx:get_config([?APP, applications], []),
|
|
||||||
[ begin
|
|
||||||
case {AppId, AppSecret} of
|
|
||||||
{undefined, _} -> ok;
|
|
||||||
{_, undefined} -> ok;
|
|
||||||
{_, _} ->
|
|
||||||
AppId1 = to_binary(AppId),
|
|
||||||
AppSecret1 = to_binary(AppSecret),
|
|
||||||
add_app(AppId1, <<"Default">>, AppSecret1, <<"Application user">>, true, undefined)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|| #{id := AppId, secret := AppSecret} <- Apps].
|
|
||||||
|
|
||||||
-spec(add_app(appid(), binary()) -> {ok, appsecret()} | {error, term()}).
|
|
||||||
add_app(AppId, Name) when is_binary(AppId) ->
|
|
||||||
add_app(AppId, Name, <<"Application user">>, true, undefined).
|
|
||||||
|
|
||||||
-spec(add_app(appid(), binary(), binary(), boolean(), integer() | undefined)
|
|
||||||
-> {ok, appsecret()}
|
|
||||||
| {error, term()}).
|
|
||||||
add_app(AppId, Name, Desc, Status, Expired) when is_binary(AppId) ->
|
|
||||||
add_app(AppId, Name, undefined, Desc, Status, Expired).
|
|
||||||
|
|
||||||
-spec(add_app(appid(), binary(), binary() | undefined, binary(), boolean(), integer() | undefined)
|
|
||||||
-> {ok, appsecret()}
|
|
||||||
| {error, term()}).
|
|
||||||
add_app(AppId, Name, Secret, Desc, Status, Expired) when is_binary(AppId) ->
|
|
||||||
Secret1 = generate_appsecret_if_need(Secret),
|
|
||||||
App = #mqtt_app{id = AppId,
|
|
||||||
secret = Secret1,
|
|
||||||
name = Name,
|
|
||||||
desc = Desc,
|
|
||||||
status = Status,
|
|
||||||
expired = Expired},
|
|
||||||
AddFun = fun() ->
|
|
||||||
case mnesia:wread({mqtt_app, AppId}) of
|
|
||||||
[] -> mnesia:write(App);
|
|
||||||
_ -> mnesia:abort(alread_existed)
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
case ekka_mnesia:transaction(?MANAGEMENT_SHARD, AddFun) of
|
|
||||||
{atomic, ok} -> {ok, Secret1};
|
|
||||||
{aborted, Reason} -> {error, Reason}
|
|
||||||
end.
|
|
||||||
|
|
||||||
force_add_app(AppId, Name, Secret, Desc, Status, Expired) ->
|
|
||||||
AddFun = fun() ->
|
|
||||||
mnesia:write(#mqtt_app{id = AppId,
|
|
||||||
secret = Secret,
|
|
||||||
name = Name,
|
|
||||||
desc = Desc,
|
|
||||||
status = Status,
|
|
||||||
expired = Expired})
|
|
||||||
end,
|
|
||||||
case ekka_mnesia:transaction(?MANAGEMENT_SHARD, AddFun) of
|
|
||||||
{atomic, ok} -> ok;
|
|
||||||
{aborted, Reason} -> {error, Reason}
|
|
||||||
end.
|
|
||||||
|
|
||||||
-spec(generate_appsecret_if_need(binary() | undefined) -> binary()).
|
|
||||||
generate_appsecret_if_need(InSecrt) when is_binary(InSecrt), byte_size(InSecrt) > 0 ->
|
|
||||||
InSecrt;
|
|
||||||
generate_appsecret_if_need(_) ->
|
|
||||||
emqx_guid:to_base62(emqx_guid:gen()).
|
|
||||||
|
|
||||||
-spec(get_appsecret(appid()) -> {appsecret() | undefined}).
|
|
||||||
get_appsecret(AppId) when is_binary(AppId) ->
|
|
||||||
case mnesia:dirty_read(mqtt_app, AppId) of
|
|
||||||
[#mqtt_app{secret = Secret}] -> Secret;
|
|
||||||
[] -> undefined
|
|
||||||
end.
|
|
||||||
|
|
||||||
-spec(lookup_app(appid()) -> undefined | {appid(), appsecret(), binary(), binary(), boolean(), integer() | undefined}).
|
|
||||||
lookup_app(AppId) when is_binary(AppId) ->
|
|
||||||
case mnesia:dirty_read(mqtt_app, AppId) of
|
|
||||||
[#mqtt_app{id = AppId,
|
|
||||||
secret = AppSecret,
|
|
||||||
name = Name,
|
|
||||||
desc = Desc,
|
|
||||||
status = Status,
|
|
||||||
expired = Expired}] -> {AppId, AppSecret, Name, Desc, Status, Expired};
|
|
||||||
[] -> undefined
|
|
||||||
end.
|
|
||||||
|
|
||||||
-spec(update_app(appid(), boolean()) -> ok | {error, term()}).
|
|
||||||
update_app(AppId, Status) ->
|
|
||||||
case mnesia:dirty_read(mqtt_app, AppId) of
|
|
||||||
[App = #mqtt_app{}] ->
|
|
||||||
Fun = fun() -> mnesia:write(App#mqtt_app{status = Status}) end,
|
|
||||||
case ekka_mnesia:transaction(?MANAGEMENT_SHARD, Fun) of
|
|
||||||
{atomic, ok} -> ok;
|
|
||||||
{aborted, Reason} -> {error, Reason}
|
|
||||||
end;
|
|
||||||
[] ->
|
|
||||||
{error, not_found}
|
|
||||||
end.
|
|
||||||
|
|
||||||
-spec(update_app(appid(), binary(), binary(), boolean(), integer() | undefined) -> ok | {error, term()}).
|
|
||||||
update_app(AppId, Name, Desc, Status, Expired) ->
|
|
||||||
case mnesia:dirty_read(mqtt_app, AppId) of
|
|
||||||
[App = #mqtt_app{}] ->
|
|
||||||
case ekka_mnesia:transaction(
|
|
||||||
?MANAGEMENT_SHARD,
|
|
||||||
fun() -> mnesia:write(App#mqtt_app{name = Name,
|
|
||||||
desc = Desc,
|
|
||||||
status = Status,
|
|
||||||
expired = Expired}) end) of
|
|
||||||
{atomic, ok} -> ok;
|
|
||||||
{aborted, Reason} -> {error, Reason}
|
|
||||||
end;
|
|
||||||
[] ->
|
|
||||||
{error, not_found}
|
|
||||||
end.
|
|
||||||
|
|
||||||
-spec(del_app(appid()) -> ok | {error, term()}).
|
|
||||||
del_app(AppId) when is_binary(AppId) ->
|
|
||||||
case ekka_mnesia:transaction(?MANAGEMENT_SHARD, fun mnesia:delete/1, [{mqtt_app, AppId}]) of
|
|
||||||
{atomic, Ok} -> Ok;
|
|
||||||
{aborted, Reason} -> {error, Reason}
|
|
||||||
end.
|
|
||||||
|
|
||||||
-spec(list_apps() -> [{appid(), appsecret(), binary(), binary(), boolean(), integer() | undefined}]).
|
|
||||||
list_apps() ->
|
|
||||||
[ {AppId, AppSecret, Name, Desc, Status, Expired} || #mqtt_app{id = AppId,
|
|
||||||
secret = AppSecret,
|
|
||||||
name = Name,
|
|
||||||
desc = Desc,
|
|
||||||
status = Status,
|
|
||||||
expired = Expired} <- ets:tab2list(mqtt_app) ].
|
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
%% Authenticate App
|
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
|
|
||||||
-spec(is_authorized(appid(), appsecret()) -> boolean()).
|
|
||||||
is_authorized(AppId, AppSecret) ->
|
|
||||||
case lookup_app(AppId) of
|
|
||||||
{_, AppSecret1, _, _, Status, Expired} ->
|
|
||||||
Status andalso is_expired(Expired) andalso AppSecret =:= AppSecret1;
|
|
||||||
_ ->
|
|
||||||
false
|
|
||||||
end.
|
|
||||||
|
|
||||||
is_expired(undefined) -> true;
|
|
||||||
is_expired(Expired) -> Expired >= erlang:system_time(second).
|
|
||||||
|
|
||||||
to_binary(L) when is_list(L) -> list_to_binary(L);
|
|
||||||
to_binary(B) when is_binary(B) -> B.
|
|
|
@ -37,7 +37,6 @@
|
||||||
, mnesia/1
|
, mnesia/1
|
||||||
, trace/1
|
, trace/1
|
||||||
, log/1
|
, log/1
|
||||||
, mgmt/1
|
|
||||||
, authz/1
|
, authz/1
|
||||||
]).
|
]).
|
||||||
|
|
||||||
|
@ -61,55 +60,6 @@ load() ->
|
||||||
is_cmd(Fun) ->
|
is_cmd(Fun) ->
|
||||||
not lists:member(Fun, [init, load, module_info]).
|
not lists:member(Fun, [init, load, module_info]).
|
||||||
|
|
||||||
mgmt(["insert", AppId, Name]) ->
|
|
||||||
case emqx_mgmt_auth:add_app(list_to_binary(AppId), list_to_binary(Name)) of
|
|
||||||
{ok, Secret} ->
|
|
||||||
emqx_ctl:print("AppSecret: ~s~n", [Secret]);
|
|
||||||
{error, already_existed} ->
|
|
||||||
emqx_ctl:print("Error: already existed~n");
|
|
||||||
{error, Reason} ->
|
|
||||||
emqx_ctl:print("Error: ~p~n", [Reason])
|
|
||||||
end;
|
|
||||||
|
|
||||||
mgmt(["lookup", AppId]) ->
|
|
||||||
case emqx_mgmt_auth:lookup_app(list_to_binary(AppId)) of
|
|
||||||
{AppId1, AppSecret, Name, Desc, Status, Expired} ->
|
|
||||||
emqx_ctl:print("app_id: ~s~nsecret: ~s~nname: ~s~ndesc: ~s~nstatus: ~s~nexpired: ~p~n",
|
|
||||||
[AppId1, AppSecret, Name, Desc, Status, Expired]);
|
|
||||||
undefined ->
|
|
||||||
emqx_ctl:print("Not Found.~n")
|
|
||||||
end;
|
|
||||||
|
|
||||||
mgmt(["update", AppId, Status]) ->
|
|
||||||
case emqx_mgmt_auth:update_app(list_to_binary(AppId), list_to_atom(Status)) of
|
|
||||||
ok ->
|
|
||||||
emqx_ctl:print("update successfully.~n");
|
|
||||||
{error, Reason} ->
|
|
||||||
emqx_ctl:print("Error: ~p~n", [Reason])
|
|
||||||
end;
|
|
||||||
|
|
||||||
mgmt(["delete", AppId]) ->
|
|
||||||
case emqx_mgmt_auth:del_app(list_to_binary(AppId)) of
|
|
||||||
ok -> emqx_ctl:print("ok~n");
|
|
||||||
{error, not_found} ->
|
|
||||||
emqx_ctl:print("Error: app not found~n");
|
|
||||||
{error, Reason} ->
|
|
||||||
emqx_ctl:print("Error: ~p~n", [Reason])
|
|
||||||
end;
|
|
||||||
|
|
||||||
mgmt(["list"]) ->
|
|
||||||
lists:foreach(fun({AppId, AppSecret, Name, Desc, Status, Expired}) ->
|
|
||||||
emqx_ctl:print("app_id: ~s, secret: ~s, name: ~s, desc: ~s, status: ~s, expired: ~p~n",
|
|
||||||
[AppId, AppSecret, Name, Desc, Status, Expired])
|
|
||||||
end, emqx_mgmt_auth:list_apps());
|
|
||||||
|
|
||||||
mgmt(_) ->
|
|
||||||
emqx_ctl:usage([{"mgmt list", "List Applications"},
|
|
||||||
{"mgmt insert <AppId> <Name>", "Add Application of REST API"},
|
|
||||||
{"mgmt update <AppId> <status>", "Update Application of REST API"},
|
|
||||||
{"mgmt lookup <AppId>", "Get Application of REST API"},
|
|
||||||
{"mgmt delete <AppId>", "Delete Application of REST API"}]).
|
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
%% @doc Node status
|
%% @doc Node status
|
||||||
|
|
||||||
|
|
|
@ -1,136 +0,0 @@
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved.
|
|
||||||
%%
|
|
||||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
%% you may not use this file except in compliance with the License.
|
|
||||||
%% You may obtain a copy of the License at
|
|
||||||
%%
|
|
||||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
%%
|
|
||||||
%% Unless required by applicable law or agreed to in writing, software
|
|
||||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
%% See the License for the specific language governing permissions and
|
|
||||||
%% limitations under the License.
|
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
-module(emqx_mgmt_http).
|
|
||||||
|
|
||||||
-export([ start_listeners/0
|
|
||||||
, stop_listeners/0
|
|
||||||
, start_listener/1
|
|
||||||
, stop_listener/1]).
|
|
||||||
|
|
||||||
%% Authorization
|
|
||||||
-export([authorize_appid/1]).
|
|
||||||
|
|
||||||
-include_lib("emqx/include/emqx.hrl").
|
|
||||||
-include_lib("emqx/include/logger.hrl").
|
|
||||||
|
|
||||||
-define(APP, emqx_management).
|
|
||||||
|
|
||||||
-define(BASE_PATH, "/api/v5").
|
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
%% Start/Stop Listeners
|
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
|
|
||||||
start_listeners() ->
|
|
||||||
lists:foreach(fun start_listener/1, listeners()).
|
|
||||||
|
|
||||||
stop_listeners() ->
|
|
||||||
lists:foreach(fun stop_listener/1, listeners()).
|
|
||||||
|
|
||||||
start_listener({Proto, Port, Options}) ->
|
|
||||||
{ok, _} = application:ensure_all_started(minirest),
|
|
||||||
Authorization = {?MODULE, authorize_appid},
|
|
||||||
RanchOptions = ranch_opts(Port, Options),
|
|
||||||
GlobalSpec = #{
|
|
||||||
openapi => "3.0.0",
|
|
||||||
info => #{title => "EMQ X API", version => "5.0.0"},
|
|
||||||
servers => [#{url => ?BASE_PATH}],
|
|
||||||
tags => [#{
|
|
||||||
name => configs,
|
|
||||||
description => <<"The query string parameter `conf_path` is of jq format.">>,
|
|
||||||
externalDocs => #{
|
|
||||||
description => "Find out more about the path syntax in jq",
|
|
||||||
url => "https://stedolan.github.io/jq/manual/"
|
|
||||||
}
|
|
||||||
}],
|
|
||||||
components => #{
|
|
||||||
schemas => #{},
|
|
||||||
securitySchemes => #{
|
|
||||||
application => #{
|
|
||||||
type => apiKey,
|
|
||||||
name => "authorization",
|
|
||||||
in => header}}}},
|
|
||||||
Minirest = #{
|
|
||||||
protocol => Proto,
|
|
||||||
base_path => ?BASE_PATH,
|
|
||||||
modules => api_modules(),
|
|
||||||
authorization => Authorization,
|
|
||||||
security => [#{application => []}],
|
|
||||||
swagger_global_spec => GlobalSpec},
|
|
||||||
MinirestOptions = maps:merge(Minirest, RanchOptions),
|
|
||||||
{ok, _} = minirest:start(listener_name(Proto), MinirestOptions),
|
|
||||||
?ULOG("Start ~p listener on ~p successfully.~n", [listener_name(Proto), Port]).
|
|
||||||
|
|
||||||
ranch_opts(Port, Options0) ->
|
|
||||||
Options = lists:foldl(
|
|
||||||
fun
|
|
||||||
({K, _V}, Acc) when K =:= max_connections orelse K =:= num_acceptors -> Acc;
|
|
||||||
({inet6, true}, Acc) -> [inet6 | Acc];
|
|
||||||
({inet6, false}, Acc) -> Acc;
|
|
||||||
({ipv6_v6only, true}, Acc) -> [{ipv6_v6only, true} | Acc];
|
|
||||||
({ipv6_v6only, false}, Acc) -> Acc;
|
|
||||||
({K, V}, Acc)->
|
|
||||||
[{K, V} | Acc]
|
|
||||||
end, [], Options0),
|
|
||||||
maps:from_list([{port, Port} | Options]).
|
|
||||||
|
|
||||||
stop_listener({Proto, Port, _}) ->
|
|
||||||
?ULOG("Stop http:management listener on ~s successfully.~n",[format(Port)]),
|
|
||||||
minirest:stop(listener_name(Proto)).
|
|
||||||
|
|
||||||
listeners() ->
|
|
||||||
[{Protocol, Port, maps:to_list(maps:without([protocol, port], Map))}
|
|
||||||
|| Map = #{protocol := Protocol,port := Port}
|
|
||||||
<- emqx:get_config([emqx_management, listeners], [])].
|
|
||||||
|
|
||||||
listener_name(Proto) ->
|
|
||||||
list_to_atom(atom_to_list(Proto) ++ ":management").
|
|
||||||
|
|
||||||
authorize_appid(Req) ->
|
|
||||||
case cowboy_req:parse_header(<<"authorization">>, Req) of
|
|
||||||
{basic, AppId, AppSecret} ->
|
|
||||||
case emqx_mgmt_auth:is_authorized(AppId, AppSecret) of
|
|
||||||
true -> ok;
|
|
||||||
false -> {401, #{<<"WWW-Authenticate">> => <<"Basic Realm=\"minirest-server\"">>}, <<"UNAUTHORIZED">>}
|
|
||||||
end;
|
|
||||||
_ ->
|
|
||||||
{401, #{<<"WWW-Authenticate">> => <<"Basic Realm=\"minirest-server\"">>}, <<"UNAUTHORIZED">>}
|
|
||||||
end.
|
|
||||||
|
|
||||||
format(Port) when is_integer(Port) ->
|
|
||||||
io_lib:format("0.0.0.0:~w", [Port]);
|
|
||||||
format({Addr, Port}) when is_list(Addr) ->
|
|
||||||
io_lib:format("~s:~w", [Addr, Port]);
|
|
||||||
format({Addr, Port}) when is_tuple(Addr) ->
|
|
||||||
io_lib:format("~s:~w", [inet:ntoa(Addr), Port]).
|
|
||||||
|
|
||||||
apps() ->
|
|
||||||
Apps = [App || {App, _, _} <- application:loaded_applications(), App =/= emqx_dashboard],
|
|
||||||
lists:filter(fun(App) ->
|
|
||||||
case re:run(atom_to_list(App), "^emqx") of
|
|
||||||
{match,[{0,4}]} -> true;
|
|
||||||
_ -> false
|
|
||||||
end
|
|
||||||
end, Apps).
|
|
||||||
|
|
||||||
-ifdef(TEST).
|
|
||||||
api_modules() ->
|
|
||||||
minirest_api:find_api_modules(apps()).
|
|
||||||
-else.
|
|
||||||
api_modules() ->
|
|
||||||
minirest_api:find_api_modules(apps()) -- [emqx_mgmt_api_apps].
|
|
||||||
-endif.
|
|
||||||
|
|
|
@ -25,23 +25,14 @@
|
||||||
-define(DE_ACT_ALARM, test_de_act_alarm).
|
-define(DE_ACT_ALARM, test_de_act_alarm).
|
||||||
|
|
||||||
all() ->
|
all() ->
|
||||||
[t_alarms_api, t_delete_alarms_api].
|
emqx_ct:all(?MODULE).
|
||||||
|
|
||||||
init_per_suite(Config) ->
|
init_per_suite(Config) ->
|
||||||
ekka_mnesia:start(),
|
emqx_mgmt_api_test_util:init_suite(),
|
||||||
emqx_mgmt_auth:mnesia(boot),
|
|
||||||
emqx_ct_helpers:start_apps([emqx_management], fun set_special_configs/1),
|
|
||||||
Config.
|
Config.
|
||||||
|
|
||||||
end_per_suite(_) ->
|
end_per_suite(_) ->
|
||||||
emqx_ct_helpers:stop_apps([emqx_management]).
|
emqx_mgmt_api_test_util:end_suite().
|
||||||
|
|
||||||
set_special_configs(emqx_management) ->
|
|
||||||
emqx_config:put([emqx_management], #{listeners => [#{protocol => http, port => 8081}],
|
|
||||||
applications =>[#{id => "admin", secret => "public"}]}),
|
|
||||||
ok;
|
|
||||||
set_special_configs(_App) ->
|
|
||||||
ok.
|
|
||||||
|
|
||||||
t_alarms_api(_) ->
|
t_alarms_api(_) ->
|
||||||
ok = emqx_alarm:activate(?ACT_ALARM),
|
ok = emqx_alarm:activate(?ACT_ALARM),
|
||||||
|
|
|
@ -17,9 +17,32 @@
|
||||||
-compile(export_all).
|
-compile(export_all).
|
||||||
-compile(nowarn_export_all).
|
-compile(nowarn_export_all).
|
||||||
|
|
||||||
-define(SERVER, "http://127.0.0.1:8081").
|
-define(SERVER, "http://127.0.0.1:18083").
|
||||||
-define(BASE_PATH, "/api/v5").
|
-define(BASE_PATH, "/api/v5").
|
||||||
|
|
||||||
|
init_suite() ->
|
||||||
|
ekka_mnesia:start(),
|
||||||
|
application:load(emqx_management),
|
||||||
|
emqx_ct_helpers:start_apps([emqx_dashboard], fun set_special_configs/1).
|
||||||
|
|
||||||
|
end_suite() ->
|
||||||
|
application:unload(emqx_management),
|
||||||
|
emqx_ct_helpers:stop_apps([emqx_dashboard]).
|
||||||
|
|
||||||
|
set_special_configs(emqx_dashboard) ->
|
||||||
|
Config = #{
|
||||||
|
default_username => <<"admin">>,
|
||||||
|
default_password => <<"public">>,
|
||||||
|
listeners => [#{
|
||||||
|
protocol => http,
|
||||||
|
port => 18083
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
emqx_config:put([emqx_dashboard], Config),
|
||||||
|
ok;
|
||||||
|
set_special_configs(_App) ->
|
||||||
|
ok.
|
||||||
|
|
||||||
request_api(Method, Url) ->
|
request_api(Method, Url) ->
|
||||||
request_api(Method, Url, [], auth_header_(), []).
|
request_api(Method, Url, [], auth_header_(), []).
|
||||||
|
|
||||||
|
@ -55,13 +78,10 @@ do_request_api(Method, Request)->
|
||||||
end.
|
end.
|
||||||
|
|
||||||
auth_header_() ->
|
auth_header_() ->
|
||||||
AppId = <<"admin">>,
|
Username = <<"admin">>,
|
||||||
AppSecret = <<"public">>,
|
Password = <<"public">>,
|
||||||
auth_header_(binary_to_list(AppId), binary_to_list(AppSecret)).
|
{ok, Token} = emqx_dashboard_admin:sign_token(Username, Password),
|
||||||
|
{"Authorization", "Bearer " ++ binary_to_list(Token)}.
|
||||||
auth_header_(User, Pass) ->
|
|
||||||
Encoded = base64:encode_to_string(lists:append([User,":",Pass])),
|
|
||||||
{"Authorization","Basic " ++ Encoded}.
|
|
||||||
|
|
||||||
api_path(Parts)->
|
api_path(Parts)->
|
||||||
?SERVER ++ filename:join([?BASE_PATH | Parts]).
|
?SERVER ++ filename:join([?BASE_PATH | Parts]).
|
||||||
|
|
|
@ -1,110 +0,0 @@
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved.
|
|
||||||
%%
|
|
||||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
%% you may not use this file except in compliance with the License.
|
|
||||||
%% You may obtain a copy of the License at
|
|
||||||
%%
|
|
||||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
%%
|
|
||||||
%% Unless required by applicable law or agreed to in writing, software
|
|
||||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
%% See the License for the specific language governing permissions and
|
|
||||||
%% limitations under the License.
|
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
-module(emqx_mgmt_apps_api_SUITE).
|
|
||||||
|
|
||||||
-compile(export_all).
|
|
||||||
-compile(nowarn_export_all).
|
|
||||||
|
|
||||||
-include_lib("eunit/include/eunit.hrl").
|
|
||||||
|
|
||||||
all() ->
|
|
||||||
emqx_ct:all(?MODULE).
|
|
||||||
|
|
||||||
init_per_suite(Config) ->
|
|
||||||
ekka_mnesia:start(),
|
|
||||||
emqx_mgmt_auth:mnesia(boot),
|
|
||||||
emqx_ct_helpers:start_apps([emqx_management], fun set_special_configs/1),
|
|
||||||
Config.
|
|
||||||
|
|
||||||
end_per_suite(_) ->
|
|
||||||
emqx_ct_helpers:stop_apps([emqx_management]).
|
|
||||||
|
|
||||||
set_special_configs(emqx_management) ->
|
|
||||||
emqx_config:put([emqx_management], #{listeners => [#{protocol => http, port => 8081}],
|
|
||||||
applications =>[#{id => "admin", secret => "public"}]}),
|
|
||||||
ok;
|
|
||||||
set_special_configs(_App) ->
|
|
||||||
ok.
|
|
||||||
|
|
||||||
t_list_app(_) ->
|
|
||||||
Path = emqx_mgmt_api_test_util:api_path(["apps"]),
|
|
||||||
{ok, Body} = emqx_mgmt_api_test_util:request_api(get, Path),
|
|
||||||
Data = emqx_json:decode(Body, [return_maps]),
|
|
||||||
AdminApp = hd(Data),
|
|
||||||
Admin = maps:get(<<"app_id">>, AdminApp),
|
|
||||||
?assertEqual(<<"admin">>, Admin).
|
|
||||||
|
|
||||||
t_get_app(_) ->
|
|
||||||
Path = emqx_mgmt_api_test_util:api_path(["apps/admin"]),
|
|
||||||
{ok, Body} = emqx_mgmt_api_test_util:request_api(get, Path),
|
|
||||||
AdminApp = emqx_json:decode(Body, [return_maps]),
|
|
||||||
?assertEqual(<<"admin">>, maps:get(<<"app_id">>, AdminApp)),
|
|
||||||
?assertEqual(<<"public">>, maps:get(<<"secret">>, AdminApp)).
|
|
||||||
|
|
||||||
t_add_app(_) ->
|
|
||||||
AuthHeader = emqx_mgmt_api_test_util:auth_header_(),
|
|
||||||
AppId = <<"test_app_id">>,
|
|
||||||
TestAppPath = emqx_mgmt_api_test_util:api_path(["apps", AppId]),
|
|
||||||
AppSecret = <<"test_app_secret">>,
|
|
||||||
|
|
||||||
%% new test app
|
|
||||||
Path = emqx_mgmt_api_test_util:api_path(["apps"]),
|
|
||||||
RequestBody = #{
|
|
||||||
app_id => AppId,
|
|
||||||
secret => AppSecret,
|
|
||||||
desc => <<"test desc">>,
|
|
||||||
name => <<"test_app_name">>,
|
|
||||||
expired => erlang:system_time(second) + 3000,
|
|
||||||
status => true
|
|
||||||
},
|
|
||||||
{ok, Body} = emqx_mgmt_api_test_util:request_api(post, Path, "", AuthHeader, RequestBody),
|
|
||||||
TestAppSecret = emqx_json:decode(Body, [return_maps]),
|
|
||||||
?assertEqual(AppSecret, maps:get(<<"secret">>, TestAppSecret)),
|
|
||||||
|
|
||||||
%% get new test app
|
|
||||||
{ok, GetApp} = emqx_mgmt_api_test_util:request_api(get, TestAppPath),
|
|
||||||
TestApp = emqx_json:decode(GetApp, [return_maps]),
|
|
||||||
?assertEqual(AppId, maps:get(<<"app_id">>, TestApp)),
|
|
||||||
?assertEqual(AppSecret, maps:get(<<"secret">>, TestApp)),
|
|
||||||
|
|
||||||
%% update app
|
|
||||||
Desc2 = <<"test desc 2">>,
|
|
||||||
Name2 = <<"test_app_name_2">>,
|
|
||||||
PutBody = #{
|
|
||||||
desc => Desc2,
|
|
||||||
name => Name2,
|
|
||||||
expired => erlang:system_time(second) + 3000,
|
|
||||||
status => false
|
|
||||||
},
|
|
||||||
{ok, PutApp} = emqx_mgmt_api_test_util:request_api(put, TestAppPath, "", AuthHeader, PutBody),
|
|
||||||
TestApp1 = emqx_json:decode(PutApp, [return_maps]),
|
|
||||||
?assertEqual(Desc2, maps:get(<<"desc">>, TestApp1)),
|
|
||||||
?assertEqual(Name2, maps:get(<<"name">>, TestApp1)),
|
|
||||||
?assertEqual(false, maps:get(<<"status">>, TestApp1)),
|
|
||||||
|
|
||||||
%% after update
|
|
||||||
{ok, GetApp2} = emqx_mgmt_api_test_util:request_api(get, TestAppPath),
|
|
||||||
TestApp2 = emqx_json:decode(GetApp2, [return_maps]),
|
|
||||||
?assertEqual(Desc2, maps:get(<<"desc">>, TestApp2)),
|
|
||||||
?assertEqual(Name2, maps:get(<<"name">>, TestApp2)),
|
|
||||||
?assertEqual(false, maps:get(<<"status">>, TestApp2)),
|
|
||||||
|
|
||||||
%% delete new app
|
|
||||||
{ok, _} = emqx_mgmt_api_test_util:request_api(delete, TestAppPath),
|
|
||||||
|
|
||||||
%% after delete
|
|
||||||
?assertEqual({error,{"HTTP/1.1",404,"Not Found"}},
|
|
||||||
emqx_mgmt_api_test_util:request_api(get, TestAppPath)).
|
|
|
@ -19,26 +19,15 @@
|
||||||
|
|
||||||
-include_lib("eunit/include/eunit.hrl").
|
-include_lib("eunit/include/eunit.hrl").
|
||||||
|
|
||||||
-define(APP, emqx_management).
|
|
||||||
|
|
||||||
all() ->
|
all() ->
|
||||||
emqx_ct:all(?MODULE).
|
emqx_ct:all(?MODULE).
|
||||||
|
|
||||||
init_per_suite(Config) ->
|
init_per_suite(Config) ->
|
||||||
ekka_mnesia:start(),
|
emqx_mgmt_api_test_util:init_suite(),
|
||||||
emqx_mgmt_auth:mnesia(boot),
|
|
||||||
emqx_ct_helpers:start_apps([emqx_management], fun set_special_configs/1),
|
|
||||||
Config.
|
Config.
|
||||||
|
|
||||||
end_per_suite(_) ->
|
end_per_suite(_) ->
|
||||||
emqx_ct_helpers:stop_apps([emqx_management]).
|
emqx_mgmt_api_test_util:end_suite().
|
||||||
|
|
||||||
set_special_configs(emqx_management) ->
|
|
||||||
emqx_config:put([emqx_management], #{listeners => [#{protocol => http, port => 8081}],
|
|
||||||
applications =>[#{id => "admin", secret => "public"}]}),
|
|
||||||
ok;
|
|
||||||
set_special_configs(_App) ->
|
|
||||||
ok.
|
|
||||||
|
|
||||||
t_clients(_) ->
|
t_clients(_) ->
|
||||||
process_flag(trap_exit, true),
|
process_flag(trap_exit, true),
|
||||||
|
|
|
@ -24,20 +24,11 @@ all() ->
|
||||||
emqx_ct:all(?MODULE).
|
emqx_ct:all(?MODULE).
|
||||||
|
|
||||||
init_per_suite(Config) ->
|
init_per_suite(Config) ->
|
||||||
ekka_mnesia:start(),
|
emqx_mgmt_api_test_util:init_suite(),
|
||||||
emqx_mgmt_auth:mnesia(boot),
|
|
||||||
emqx_ct_helpers:start_apps([emqx_management], fun set_special_configs/1),
|
|
||||||
Config.
|
Config.
|
||||||
|
|
||||||
end_per_suite(_) ->
|
end_per_suite(_) ->
|
||||||
emqx_ct_helpers:stop_apps([emqx_management]).
|
emqx_mgmt_api_test_util:end_suite().
|
||||||
|
|
||||||
set_special_configs(emqx_management) ->
|
|
||||||
emqx_config:put([emqx_management], #{listeners => [#{protocol => http, port => 8081}],
|
|
||||||
applications =>[#{id => "admin", secret => "public"}]}),
|
|
||||||
ok;
|
|
||||||
set_special_configs(_App) ->
|
|
||||||
ok.
|
|
||||||
|
|
||||||
t_list_listeners(_) ->
|
t_list_listeners(_) ->
|
||||||
Path = emqx_mgmt_api_test_util:api_path(["listeners"]),
|
Path = emqx_mgmt_api_test_util:api_path(["listeners"]),
|
||||||
|
|
|
@ -24,20 +24,11 @@ all() ->
|
||||||
emqx_ct:all(?MODULE).
|
emqx_ct:all(?MODULE).
|
||||||
|
|
||||||
init_per_suite(Config) ->
|
init_per_suite(Config) ->
|
||||||
ekka_mnesia:start(),
|
emqx_mgmt_api_test_util:init_suite(),
|
||||||
emqx_mgmt_auth:mnesia(boot),
|
|
||||||
emqx_ct_helpers:start_apps([emqx_management], fun set_special_configs/1),
|
|
||||||
Config.
|
Config.
|
||||||
|
|
||||||
end_per_suite(_) ->
|
end_per_suite(_) ->
|
||||||
emqx_ct_helpers:stop_apps([emqx_management]).
|
emqx_mgmt_api_test_util:end_suite().
|
||||||
|
|
||||||
set_special_configs(emqx_management) ->
|
|
||||||
emqx_config:put([emqx_management], #{listeners => [#{protocol => http, port => 8081}],
|
|
||||||
applications =>[#{id => "admin", secret => "public"}]}),
|
|
||||||
ok;
|
|
||||||
set_special_configs(_App) ->
|
|
||||||
ok.
|
|
||||||
|
|
||||||
t_metrics_api(_) ->
|
t_metrics_api(_) ->
|
||||||
MetricsPath = emqx_mgmt_api_test_util:api_path(["metrics?aggregate=true"]),
|
MetricsPath = emqx_mgmt_api_test_util:api_path(["metrics?aggregate=true"]),
|
||||||
|
|
|
@ -24,20 +24,11 @@ all() ->
|
||||||
emqx_ct:all(?MODULE).
|
emqx_ct:all(?MODULE).
|
||||||
|
|
||||||
init_per_suite(Config) ->
|
init_per_suite(Config) ->
|
||||||
ekka_mnesia:start(),
|
emqx_mgmt_api_test_util:init_suite(),
|
||||||
emqx_mgmt_auth:mnesia(boot),
|
|
||||||
emqx_ct_helpers:start_apps([emqx_management], fun set_special_configs/1),
|
|
||||||
Config.
|
Config.
|
||||||
|
|
||||||
end_per_suite(_) ->
|
end_per_suite(_) ->
|
||||||
emqx_ct_helpers:stop_apps([emqx_management]).
|
emqx_mgmt_api_test_util:end_suite().
|
||||||
|
|
||||||
set_special_configs(emqx_management) ->
|
|
||||||
emqx_config:put([emqx_management], #{listeners => [#{protocol => http, port => 8081}],
|
|
||||||
applications =>[#{id => "admin", secret => "public"}]}),
|
|
||||||
ok;
|
|
||||||
set_special_configs(_App) ->
|
|
||||||
ok.
|
|
||||||
|
|
||||||
t_nodes_api(_) ->
|
t_nodes_api(_) ->
|
||||||
NodesPath = emqx_mgmt_api_test_util:api_path(["nodes"]),
|
NodesPath = emqx_mgmt_api_test_util:api_path(["nodes"]),
|
||||||
|
|
|
@ -26,26 +26,15 @@
|
||||||
-define(TOPIC1, <<"api_topic1">>).
|
-define(TOPIC1, <<"api_topic1">>).
|
||||||
-define(TOPIC2, <<"api_topic2">>).
|
-define(TOPIC2, <<"api_topic2">>).
|
||||||
|
|
||||||
|
|
||||||
all() ->
|
all() ->
|
||||||
emqx_ct:all(?MODULE).
|
emqx_ct:all(?MODULE).
|
||||||
|
|
||||||
init_per_suite(Config) ->
|
init_per_suite(Config) ->
|
||||||
ekka_mnesia:start(),
|
emqx_mgmt_api_test_util:init_suite(),
|
||||||
emqx_mgmt_auth:mnesia(boot),
|
|
||||||
emqx_ct_helpers:start_apps([emqx_management], fun set_special_configs/1),
|
|
||||||
Config.
|
Config.
|
||||||
|
|
||||||
|
|
||||||
end_per_suite(_) ->
|
end_per_suite(_) ->
|
||||||
emqx_ct_helpers:stop_apps([emqx_management]).
|
emqx_mgmt_api_test_util:end_suite().
|
||||||
|
|
||||||
set_special_configs(emqx_management) ->
|
|
||||||
emqx_config:put([emqx_management], #{listeners => [#{protocol => http, port => 8081}],
|
|
||||||
applications =>[#{id => "admin", secret => "public"}]}),
|
|
||||||
ok;
|
|
||||||
set_special_configs(_App) ->
|
|
||||||
ok.
|
|
||||||
|
|
||||||
t_publish_api(_) ->
|
t_publish_api(_) ->
|
||||||
{ok, Client} = emqtt:start_link(#{username => <<"api_username">>, clientid => <<"api_clientid">>}),
|
{ok, Client} = emqtt:start_link(#{username => <<"api_username">>, clientid => <<"api_clientid">>}),
|
||||||
|
|
|
@ -24,20 +24,11 @@ all() ->
|
||||||
emqx_ct:all(?MODULE).
|
emqx_ct:all(?MODULE).
|
||||||
|
|
||||||
init_per_suite(Config) ->
|
init_per_suite(Config) ->
|
||||||
ekka_mnesia:start(),
|
emqx_mgmt_api_test_util:init_suite(),
|
||||||
emqx_mgmt_auth:mnesia(boot),
|
|
||||||
emqx_ct_helpers:start_apps([emqx_management], fun set_special_configs/1),
|
|
||||||
Config.
|
Config.
|
||||||
|
|
||||||
end_per_suite(_) ->
|
end_per_suite(_) ->
|
||||||
emqx_ct_helpers:stop_apps([emqx_management]).
|
emqx_mgmt_api_test_util:end_suite().
|
||||||
|
|
||||||
set_special_configs(emqx_management) ->
|
|
||||||
emqx_config:put([emqx_management], #{listeners => [#{protocol => http, port => 8081}],
|
|
||||||
applications =>[#{id => "admin", secret => "public"}]}),
|
|
||||||
ok;
|
|
||||||
set_special_configs(_App) ->
|
|
||||||
ok.
|
|
||||||
|
|
||||||
t_nodes_api(_) ->
|
t_nodes_api(_) ->
|
||||||
Topic = <<"test_topic">>,
|
Topic = <<"test_topic">>,
|
||||||
|
|
|
@ -24,20 +24,11 @@ all() ->
|
||||||
emqx_ct:all(?MODULE).
|
emqx_ct:all(?MODULE).
|
||||||
|
|
||||||
init_per_suite(Config) ->
|
init_per_suite(Config) ->
|
||||||
ekka_mnesia:start(),
|
emqx_mgmt_api_test_util:init_suite(),
|
||||||
emqx_mgmt_auth:mnesia(boot),
|
|
||||||
emqx_ct_helpers:start_apps([emqx_management], fun set_special_configs/1),
|
|
||||||
Config.
|
Config.
|
||||||
|
|
||||||
end_per_suite(_) ->
|
end_per_suite(_) ->
|
||||||
emqx_ct_helpers:stop_apps([emqx_management]).
|
emqx_mgmt_api_test_util:end_suite().
|
||||||
|
|
||||||
set_special_configs(emqx_management) ->
|
|
||||||
emqx_config:put([emqx_management], #{listeners => [#{protocol => http, port => 8081}],
|
|
||||||
applications =>[#{id => "admin", secret => "public"}]}),
|
|
||||||
ok;
|
|
||||||
set_special_configs(_App) ->
|
|
||||||
ok.
|
|
||||||
|
|
||||||
t_stats_api(_) ->
|
t_stats_api(_) ->
|
||||||
StatsPath = emqx_mgmt_api_test_util:api_path(["stats?aggregate=true"]),
|
StatsPath = emqx_mgmt_api_test_util:api_path(["stats?aggregate=true"]),
|
||||||
|
|
|
@ -27,26 +27,15 @@
|
||||||
-define(TOPIC1, <<"0000">>).
|
-define(TOPIC1, <<"0000">>).
|
||||||
-define(TOPIC2, <<"0001">>).
|
-define(TOPIC2, <<"0001">>).
|
||||||
|
|
||||||
|
|
||||||
all() ->
|
all() ->
|
||||||
emqx_ct:all(?MODULE).
|
emqx_ct:all(?MODULE).
|
||||||
|
|
||||||
init_per_suite(Config) ->
|
init_per_suite(Config) ->
|
||||||
ekka_mnesia:start(),
|
emqx_mgmt_api_test_util:init_suite(),
|
||||||
emqx_mgmt_auth:mnesia(boot),
|
|
||||||
emqx_ct_helpers:start_apps([emqx_management], fun set_special_configs/1),
|
|
||||||
Config.
|
Config.
|
||||||
|
|
||||||
|
|
||||||
end_per_suite(_) ->
|
end_per_suite(_) ->
|
||||||
emqx_ct_helpers:stop_apps([emqx_management]).
|
emqx_mgmt_api_test_util:end_suite().
|
||||||
|
|
||||||
set_special_configs(emqx_management) ->
|
|
||||||
emqx_config:put([emqx_management], #{listeners => [#{protocol => http, port => 8081}],
|
|
||||||
applications =>[#{id => "admin", secret => "public"}]}),
|
|
||||||
ok;
|
|
||||||
set_special_configs(_App) ->
|
|
||||||
ok.
|
|
||||||
|
|
||||||
t_subscription_api(_) ->
|
t_subscription_api(_) ->
|
||||||
{ok, Client} = emqtt:start_link(#{username => ?USERNAME, clientid => ?CLIENTID}),
|
{ok, Client} = emqtt:start_link(#{username => ?USERNAME, clientid => ?CLIENTID}),
|
||||||
|
|
|
@ -94,8 +94,6 @@ spec:
|
||||||
containerPort: {{ .Values.emqxConfig.EMQX_LISTENERS__TCP__DEFAULT | default 1883 }}
|
containerPort: {{ .Values.emqxConfig.EMQX_LISTENERS__TCP__DEFAULT | default 1883 }}
|
||||||
- name: mqttssl
|
- name: mqttssl
|
||||||
containerPort: {{ .Values.emqxConfig.EMQX_LISTENERS__SSL__DEFAULT | default 8883 }}
|
containerPort: {{ .Values.emqxConfig.EMQX_LISTENERS__SSL__DEFAULT | default 8883 }}
|
||||||
- name: mgmt
|
|
||||||
containerPort: {{ .Values.emqxConfig.EMQX_MANAGEMENT__LISTENER__HTTP | default 8081 }}
|
|
||||||
- name: ws
|
- name: ws
|
||||||
containerPort: {{ .Values.emqxConfig.EMQX_LISTENERS__WS__DEFAULT | default 8083 }}
|
containerPort: {{ .Values.emqxConfig.EMQX_LISTENERS__WS__DEFAULT | default 8083 }}
|
||||||
- name: wss
|
- name: wss
|
||||||
|
@ -140,7 +138,7 @@ spec:
|
||||||
readinessProbe:
|
readinessProbe:
|
||||||
httpGet:
|
httpGet:
|
||||||
path: /api/v5/status
|
path: /api/v5/status
|
||||||
port: {{ .Values.emqxConfig.EMQX_MANAGEMENT__LISTENER__HTTP | default 8081 }}
|
port: {{ .Values.emqxConfig.EMQX_DASHBOARD__LISTENER__HTTP | default 18083 }}
|
||||||
initialDelaySeconds: 5
|
initialDelaySeconds: 5
|
||||||
periodSeconds: 5
|
periodSeconds: 5
|
||||||
{{- with .Values.nodeSelector }}
|
{{- with .Values.nodeSelector }}
|
||||||
|
|
|
@ -55,15 +55,6 @@ spec:
|
||||||
{{- else if eq .Values.service.type "ClusterIP" }}
|
{{- else if eq .Values.service.type "ClusterIP" }}
|
||||||
nodePort: null
|
nodePort: null
|
||||||
{{- end }}
|
{{- end }}
|
||||||
- name: mgmt
|
|
||||||
port: {{ .Values.service.mgmt | default 8081 }}
|
|
||||||
protocol: TCP
|
|
||||||
targetPort: mgmt
|
|
||||||
{{- if and (or (eq .Values.service.type "NodePort") (eq .Values.service.type "LoadBalancer")) (not (empty .Values.service.nodePorts.mgmt)) }}
|
|
||||||
nodePort: {{ .Values.service.nodePorts.mgmt }}
|
|
||||||
{{- else if eq .Values.service.type "ClusterIP" }}
|
|
||||||
nodePort: null
|
|
||||||
{{- end }}
|
|
||||||
- name: ws
|
- name: ws
|
||||||
port: {{ .Values.service.ws | default 8083 }}
|
port: {{ .Values.service.ws | default 8083 }}
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
|
@ -136,10 +127,6 @@ spec:
|
||||||
port: {{ .Values.service.mqttssl | default 8883 }}
|
port: {{ .Values.service.mqttssl | default 8883 }}
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
targetPort: mqttssl
|
targetPort: mqttssl
|
||||||
- name: mgmt
|
|
||||||
port: {{ .Values.service.mgmt | default 8081 }}
|
|
||||||
protocol: TCP
|
|
||||||
targetPort: mgmt
|
|
||||||
- name: ws
|
- name: ws
|
||||||
port: {{ .Values.service.ws | default 8083 }}
|
port: {{ .Values.service.ws | default 8083 }}
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
|
|
Loading…
Reference in New Issue