refactor: close managment http 8081 (#5564)

This commit is contained in:
DDDHuang 2021-08-27 13:46:39 +08:00 committed by GitHub
parent 4c1e0802d9
commit 86231f795d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 88 additions and 1626 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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">>}),

View File

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

View File

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

View File

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

View File

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

View File

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