Merge pull request #6248 from HJianBo/gw-improve-apis-1
Add authentication data management APIs for gateway
This commit is contained in:
commit
50542ec441
|
@ -65,6 +65,17 @@
|
||||||
, response_users_example/0
|
, response_users_example/0
|
||||||
]).
|
]).
|
||||||
|
|
||||||
|
%% export these funcs for gateway
|
||||||
|
-export([ list_users/3
|
||||||
|
, add_user/3
|
||||||
|
, delete_user/3
|
||||||
|
, find_user/3
|
||||||
|
, update_user/4
|
||||||
|
, serialize_error/1
|
||||||
|
]).
|
||||||
|
|
||||||
|
-elvis([{elvis_style, god_modules, disable}]).
|
||||||
|
|
||||||
api_spec() ->
|
api_spec() ->
|
||||||
emqx_dashboard_swagger:spec(?MODULE, #{check_schema => true}).
|
emqx_dashboard_swagger:spec(?MODULE, #{check_schema => true}).
|
||||||
|
|
||||||
|
@ -785,11 +796,12 @@ add_user(_, _, #{<<"user_id">> := _}) ->
|
||||||
add_user(_, _, _) ->
|
add_user(_, _, _) ->
|
||||||
serialize_error({missing_parameter, user_id}).
|
serialize_error({missing_parameter, user_id}).
|
||||||
|
|
||||||
update_user(ChainName, AuthenticatorID, UserID, UserInfo) ->
|
update_user(ChainName, AuthenticatorID, UserID, UserInfo0) ->
|
||||||
case maps:with([<<"password">>, <<"is_superuser">>], UserInfo) =:= #{} of
|
case maps:with([<<"password">>, <<"is_superuser">>], UserInfo0) =:= #{} of
|
||||||
true ->
|
true ->
|
||||||
serialize_error({missing_parameter, password});
|
serialize_error({missing_parameter, password});
|
||||||
false ->
|
false ->
|
||||||
|
UserInfo = emqx_map_lib:safe_atom_key_map(UserInfo0),
|
||||||
case emqx_authentication:update_user(ChainName, AuthenticatorID, UserID, UserInfo) of
|
case emqx_authentication:update_user(ChainName, AuthenticatorID, UserID, UserInfo) of
|
||||||
{ok, User} ->
|
{ok, User} ->
|
||||||
{200, User};
|
{200, User};
|
||||||
|
|
|
@ -18,21 +18,34 @@
|
||||||
|
|
||||||
-behaviour(minirest_api).
|
-behaviour(minirest_api).
|
||||||
|
|
||||||
|
-include_lib("typerefl/include/types.hrl").
|
||||||
|
|
||||||
|
-define(BAD_REQUEST, 'BAD_REQUEST').
|
||||||
|
-define(NOT_FOUND, 'NOT_FOUND').
|
||||||
|
-define(INTERNAL_ERROR, 'INTERNAL_SERVER_ERROR').
|
||||||
|
|
||||||
|
-import(hoconsc, [mk/2, ref/2]).
|
||||||
|
-import(emqx_dashboard_swagger, [error_codes/2]).
|
||||||
|
|
||||||
-import(emqx_gateway_http,
|
-import(emqx_gateway_http,
|
||||||
[ return_http_error/2
|
[ return_http_error/2
|
||||||
, schema_bad_request/0
|
|
||||||
, schema_not_found/0
|
|
||||||
, schema_internal_error/0
|
|
||||||
, schema_no_content/0
|
|
||||||
, with_gateway/2
|
, with_gateway/2
|
||||||
|
, with_authn/2
|
||||||
, checks/2
|
, checks/2
|
||||||
]).
|
]).
|
||||||
|
|
||||||
%% minirest behaviour callbacks
|
%% minirest/dashbaord_swagger behaviour callbacks
|
||||||
-export([api_spec/0]).
|
-export([ api_spec/0
|
||||||
|
, paths/0
|
||||||
|
, schema/1
|
||||||
|
]).
|
||||||
|
|
||||||
%% http handlers
|
%% http handlers
|
||||||
-export([authn/2]).
|
-export([ authn/2
|
||||||
|
, users/2
|
||||||
|
, users_insta/2
|
||||||
|
, import_users/2
|
||||||
|
]).
|
||||||
|
|
||||||
%% internal export for emqx_gateway_api_listeners module
|
%% internal export for emqx_gateway_api_listeners module
|
||||||
-export([schema_authn/0]).
|
-export([schema_authn/0]).
|
||||||
|
@ -42,10 +55,13 @@
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
api_spec() ->
|
api_spec() ->
|
||||||
{metadata(apis()), []}.
|
emqx_dashboard_swagger:spec(?MODULE, #{check_schema => true}).
|
||||||
|
|
||||||
apis() ->
|
paths() ->
|
||||||
[ {"/gateway/:name/authentication", authn}
|
[ "/gateway/:name/authentication"
|
||||||
|
, "/gateway/:name/authentication/users"
|
||||||
|
, "/gateway/:name/authentication/users/:uid"
|
||||||
|
, "/gateway/:name/authentication/import_users"
|
||||||
].
|
].
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
@ -66,6 +82,7 @@ authn(get, #{bindings := #{name := Name0}}) ->
|
||||||
authn(put, #{bindings := #{name := Name0},
|
authn(put, #{bindings := #{name := Name0},
|
||||||
body := Body}) ->
|
body := Body}) ->
|
||||||
with_gateway(Name0, fun(GwName, _) ->
|
with_gateway(Name0, fun(GwName, _) ->
|
||||||
|
%% TODO: return the authn instances?
|
||||||
ok = emqx_gateway_http:update_authn(GwName, Body),
|
ok = emqx_gateway_http:update_authn(GwName, Body),
|
||||||
{204}
|
{204}
|
||||||
end);
|
end);
|
||||||
|
@ -73,6 +90,7 @@ authn(put, #{bindings := #{name := Name0},
|
||||||
authn(post, #{bindings := #{name := Name0},
|
authn(post, #{bindings := #{name := Name0},
|
||||||
body := Body}) ->
|
body := Body}) ->
|
||||||
with_gateway(Name0, fun(GwName, _) ->
|
with_gateway(Name0, fun(GwName, _) ->
|
||||||
|
%% TODO: return the authn instances?
|
||||||
ok = emqx_gateway_http:add_authn(GwName, Body),
|
ok = emqx_gateway_http:add_authn(GwName, Body),
|
||||||
{204}
|
{204}
|
||||||
end);
|
end);
|
||||||
|
@ -83,67 +101,212 @@ authn(delete, #{bindings := #{name := Name0}}) ->
|
||||||
{204}
|
{204}
|
||||||
end).
|
end).
|
||||||
|
|
||||||
|
users(get, #{bindings := #{name := Name0}, query_string := Qs}) ->
|
||||||
|
with_authn(Name0, fun(_GwName, #{id := AuthId,
|
||||||
|
chain_name := ChainName}) ->
|
||||||
|
emqx_authn_api:list_users(ChainName, AuthId, page_pramas(Qs))
|
||||||
|
end);
|
||||||
|
users(post, #{bindings := #{name := Name0},
|
||||||
|
body := Body}) ->
|
||||||
|
with_authn(Name0, fun(_GwName, #{id := AuthId,
|
||||||
|
chain_name := ChainName}) ->
|
||||||
|
emqx_authn_api:add_user(ChainName, AuthId, Body)
|
||||||
|
end).
|
||||||
|
|
||||||
|
users_insta(get, #{bindings := #{name := Name0, uid := UserId}}) ->
|
||||||
|
with_authn(Name0, fun(_GwName, #{id := AuthId,
|
||||||
|
chain_name := ChainName}) ->
|
||||||
|
emqx_authn_api:find_user(ChainName, AuthId, UserId)
|
||||||
|
end);
|
||||||
|
users_insta(put, #{bindings := #{name := Name0, uid := UserId},
|
||||||
|
body := Body}) ->
|
||||||
|
with_authn(Name0, fun(_GwName, #{id := AuthId,
|
||||||
|
chain_name := ChainName}) ->
|
||||||
|
emqx_authn_api:update_user(ChainName, AuthId, UserId, Body)
|
||||||
|
end);
|
||||||
|
users_insta(delete, #{bindings := #{name := Name0, uid := UserId}}) ->
|
||||||
|
with_authn(Name0, fun(_GwName, #{id := AuthId,
|
||||||
|
chain_name := ChainName}) ->
|
||||||
|
emqx_authn_api:delete_user(ChainName, AuthId, UserId)
|
||||||
|
end).
|
||||||
|
|
||||||
|
import_users(post, #{bindings := #{name := Name0},
|
||||||
|
body := Body}) ->
|
||||||
|
with_authn(Name0, fun(_GwName, #{id := AuthId,
|
||||||
|
chain_name := ChainName}) ->
|
||||||
|
case maps:get(<<"filename">>, Body, undefined) of
|
||||||
|
undefined ->
|
||||||
|
emqx_authn_api:serialize_error({missing_parameter, filename});
|
||||||
|
Filename ->
|
||||||
|
case emqx_authentication:import_users(
|
||||||
|
ChainName, AuthId, Filename) of
|
||||||
|
ok -> {204};
|
||||||
|
{error, Reason} ->
|
||||||
|
emqx_authn_api:serialize_error(Reason)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end).
|
||||||
|
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Utils
|
||||||
|
|
||||||
|
page_pramas(Qs) ->
|
||||||
|
maps:with([<<"page">>, <<"limit">>], Qs).
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
%% Swagger defines
|
%% Swagger defines
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
metadata(APIs) ->
|
|
||||||
metadata(APIs, []).
|
|
||||||
metadata([], APIAcc) ->
|
|
||||||
lists:reverse(APIAcc);
|
|
||||||
metadata([{Path, Fun}|More], APIAcc) ->
|
|
||||||
Methods = [get, post, put, delete, patch],
|
|
||||||
Mds = lists:foldl(fun(M, Acc) ->
|
|
||||||
try
|
|
||||||
Acc#{M => swagger(Path, M)}
|
|
||||||
catch
|
|
||||||
error : function_clause ->
|
|
||||||
Acc
|
|
||||||
end
|
|
||||||
end, #{}, Methods),
|
|
||||||
metadata(More, [{Path, Mds, Fun} | APIAcc]).
|
|
||||||
|
|
||||||
swagger("/gateway/:name/authentication", get) ->
|
schema("/gateway/:name/authentication") ->
|
||||||
|
#{ 'operationId' => authn,
|
||||||
|
get =>
|
||||||
#{ description => <<"Get the gateway authentication">>
|
#{ description => <<"Get the gateway authentication">>
|
||||||
, parameters => params_gateway_name_in_path()
|
, parameters => params_gateway_name_in_path()
|
||||||
, responses =>
|
, responses =>
|
||||||
#{ <<"400">> => schema_bad_request()
|
#{ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>)
|
||||||
, <<"404">> => schema_not_found()
|
, 404 => error_codes([?NOT_FOUND], <<"Not Found">>)
|
||||||
, <<"500">> => schema_internal_error()
|
, 500 => error_codes([?INTERNAL_ERROR],
|
||||||
, <<"200">> => schema_authn()
|
<<"Ineternal Server Error">>)
|
||||||
, <<"204">> => schema_no_content()
|
, 200 => schema_authn()
|
||||||
|
, 204 => <<"Authentication does not initiated">>
|
||||||
}
|
}
|
||||||
};
|
},
|
||||||
swagger("/gateway/:name/authentication", put) ->
|
put =>
|
||||||
#{ description => <<"Update authentication for the gateway">>
|
#{ description => <<"Update authentication for the gateway">>
|
||||||
, parameters => params_gateway_name_in_path()
|
, parameters => params_gateway_name_in_path()
|
||||||
, requestBody => schema_authn()
|
, 'requestBody' => schema_authn()
|
||||||
, responses =>
|
, responses =>
|
||||||
#{ <<"400">> => schema_bad_request()
|
#{ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>)
|
||||||
, <<"404">> => schema_not_found()
|
, 404 => error_codes([?NOT_FOUND], <<"Not Found">>)
|
||||||
, <<"500">> => schema_internal_error()
|
, 500 => error_codes([?INTERNAL_ERROR],
|
||||||
, <<"204">> => schema_no_content()
|
<<"Ineternal Server Error">>)
|
||||||
|
, 204 => <<"Updated">> %% XXX: ??? return the updated object
|
||||||
}
|
}
|
||||||
};
|
},
|
||||||
swagger("/gateway/:name/authentication", post) ->
|
post =>
|
||||||
#{ description => <<"Add authentication for the gateway">>
|
#{ description => <<"Add authentication for the gateway">>
|
||||||
, parameters => params_gateway_name_in_path()
|
, parameters => params_gateway_name_in_path()
|
||||||
, requestBody => schema_authn()
|
, 'requestBody' => schema_authn()
|
||||||
, responses =>
|
, responses =>
|
||||||
#{ <<"400">> => schema_bad_request()
|
#{ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>)
|
||||||
, <<"404">> => schema_not_found()
|
, 404 => error_codes([?NOT_FOUND], <<"Not Found">>)
|
||||||
, <<"500">> => schema_internal_error()
|
, 500 => error_codes([?INTERNAL_ERROR],
|
||||||
, <<"204">> => schema_no_content()
|
<<"Ineternal Server Error">>)
|
||||||
|
, 204 => <<"Added">>
|
||||||
}
|
}
|
||||||
};
|
},
|
||||||
swagger("/gateway/:name/authentication", delete) ->
|
delete =>
|
||||||
#{ description => <<"Remove the gateway authentication">>
|
#{ description => <<"Remove the gateway authentication">>
|
||||||
, parameters => params_gateway_name_in_path()
|
, parameters => params_gateway_name_in_path()
|
||||||
, responses =>
|
, responses =>
|
||||||
#{ <<"400">> => schema_bad_request()
|
#{ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>)
|
||||||
, <<"404">> => schema_not_found()
|
, 404 => error_codes([?NOT_FOUND], <<"Not Found">>)
|
||||||
, <<"500">> => schema_internal_error()
|
, 500 => error_codes([?INTERNAL_ERROR],
|
||||||
, <<"204">> => schema_no_content()
|
<<"Ineternal Server Error">>)
|
||||||
|
, 204 => <<"Deleted">>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
schema("/gateway/:name/authentication/users") ->
|
||||||
|
#{ 'operationId' => users
|
||||||
|
, get =>
|
||||||
|
#{ description => <<"Get the users for the authentication">>
|
||||||
|
, parameters => params_gateway_name_in_path() ++
|
||||||
|
params_paging_in_qs()
|
||||||
|
, responses =>
|
||||||
|
#{ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>)
|
||||||
|
, 404 => error_codes([?NOT_FOUND], <<"Not Found">>)
|
||||||
|
, 500 => error_codes([?INTERNAL_ERROR],
|
||||||
|
<<"Ineternal Server Error">>)
|
||||||
|
, 200 => emqx_dashboard_swagger:schema_with_example(
|
||||||
|
ref(emqx_authn_api, response_user),
|
||||||
|
emqx_authn_api:response_user_examples())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
post =>
|
||||||
|
#{ description => <<"Add user for the authentication">>
|
||||||
|
, parameters => params_gateway_name_in_path()
|
||||||
|
, 'requestBody' => emqx_dashboard_swagger:schema_with_examples(
|
||||||
|
ref(emqx_authn_api, request_user_create),
|
||||||
|
emqx_authn_api:request_user_create_examples())
|
||||||
|
, responses =>
|
||||||
|
#{ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>)
|
||||||
|
, 404 => error_codes([?NOT_FOUND], <<"Not Found">>)
|
||||||
|
, 500 => error_codes([?INTERNAL_ERROR],
|
||||||
|
<<"Ineternal Server Error">>)
|
||||||
|
, 201 => emqx_dashboard_swagger:schema_with_example(
|
||||||
|
ref(emqx_authn_api, response_user),
|
||||||
|
emqx_authn_api:response_user_examples())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
schema("/gateway/:name/authentication/users/:uid") ->
|
||||||
|
#{ 'operationId' => users_insta
|
||||||
|
, get =>
|
||||||
|
#{ description => <<"Get user info from the gateway "
|
||||||
|
"authentication">>
|
||||||
|
, parameters => params_gateway_name_in_path() ++
|
||||||
|
params_userid_in_path()
|
||||||
|
, responses =>
|
||||||
|
#{ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>)
|
||||||
|
, 404 => error_codes([?NOT_FOUND], <<"Not Found">>)
|
||||||
|
, 500 => error_codes([?INTERNAL_ERROR],
|
||||||
|
<<"Ineternal Server Error">>)
|
||||||
|
, 200 => emqx_dashboard_swagger:schema_with_example(
|
||||||
|
ref(emqx_authn_api, response_user),
|
||||||
|
emqx_authn_api:response_user_examples())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
put =>
|
||||||
|
#{ description => <<"Update the user info for the gateway "
|
||||||
|
"authentication">>
|
||||||
|
, parameters => params_gateway_name_in_path() ++
|
||||||
|
params_userid_in_path()
|
||||||
|
, 'requestBody' => emqx_dashboard_swagger:schema_with_examples(
|
||||||
|
ref(emqx_authn_api, request_user_update),
|
||||||
|
emqx_authn_api:request_user_update_examples())
|
||||||
|
, responses =>
|
||||||
|
#{ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>)
|
||||||
|
, 404 => error_codes([?NOT_FOUND], <<"Not Found">>)
|
||||||
|
, 500 => error_codes([?INTERNAL_ERROR],
|
||||||
|
<<"Ineternal Server Error">>)
|
||||||
|
, 200 => emqx_dashboard_swagger:schema_with_example(
|
||||||
|
ref(emqx_authn_api, response_user),
|
||||||
|
emqx_authn_api:response_user_examples())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
delete =>
|
||||||
|
#{ description => <<"Delete the user for the gateway "
|
||||||
|
"authentication">>
|
||||||
|
, parameters => params_gateway_name_in_path() ++
|
||||||
|
params_userid_in_path()
|
||||||
|
, responses =>
|
||||||
|
#{ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>)
|
||||||
|
, 404 => error_codes([?NOT_FOUND], <<"Not Found">>)
|
||||||
|
, 500 => error_codes([?INTERNAL_ERROR],
|
||||||
|
<<"Ineternal Server Error">>)
|
||||||
|
, 204 => <<"User Deleted">>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
schema("/gateway/:name/authentication/import_users") ->
|
||||||
|
#{ 'operationId' => import_users
|
||||||
|
, post =>
|
||||||
|
#{ description => <<"Import users into the gateway authentication">>
|
||||||
|
, parameters => params_gateway_name_in_path()
|
||||||
|
, 'requestBody' => emqx_dashboard_swagger:schema_with_examples(
|
||||||
|
ref(emqx_authn_api, request_import_users),
|
||||||
|
emqx_authn_api:request_import_users_examples()
|
||||||
|
)
|
||||||
|
, responses =>
|
||||||
|
#{ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>)
|
||||||
|
, 404 => error_codes([?NOT_FOUND], <<"Not Found">>)
|
||||||
|
, 500 => error_codes([?INTERNAL_ERROR],
|
||||||
|
<<"Ineternal Server Error">>)
|
||||||
|
%% XXX: Put a hint message into 204 return ?
|
||||||
|
, 204 => <<"Imported">>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}.
|
}.
|
||||||
|
|
||||||
|
@ -151,19 +314,38 @@ swagger("/gateway/:name/authentication", delete) ->
|
||||||
%% params defines
|
%% params defines
|
||||||
|
|
||||||
params_gateway_name_in_path() ->
|
params_gateway_name_in_path() ->
|
||||||
[#{ name => name
|
[{name,
|
||||||
, in => path
|
mk(binary(),
|
||||||
, schema => #{type => string}
|
#{ in => path
|
||||||
, required => true
|
, desc => <<"Gateway Name">>
|
||||||
}].
|
})}
|
||||||
|
].
|
||||||
|
|
||||||
|
params_userid_in_path() ->
|
||||||
|
[{uid, mk(binary(),
|
||||||
|
#{ in => path
|
||||||
|
, desc => <<"User ID">>
|
||||||
|
})}
|
||||||
|
].
|
||||||
|
|
||||||
|
params_paging_in_qs() ->
|
||||||
|
[{page, mk(integer(),
|
||||||
|
#{ in => query
|
||||||
|
, nullable => true
|
||||||
|
, desc => <<"Page Index">>
|
||||||
|
})},
|
||||||
|
{limit, mk(integer(),
|
||||||
|
#{ in => query
|
||||||
|
, nullable => true
|
||||||
|
, desc => <<"Page Limit">>
|
||||||
|
})}
|
||||||
|
].
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
%% schemas
|
%% schemas
|
||||||
|
|
||||||
schema_authn() ->
|
schema_authn() ->
|
||||||
#{ description => <<"OK">>
|
emqx_dashboard_swagger:schema_with_examples(
|
||||||
, content => #{
|
emqx_authn_schema:authenticator_type(),
|
||||||
'application/json' => #{
|
emqx_authn_api:authenticator_examples()
|
||||||
schema => minirest:ref(<<"AuthenticatorInstance">>)
|
).
|
||||||
}}
|
|
||||||
}.
|
|
||||||
|
|
|
@ -18,25 +18,41 @@
|
||||||
|
|
||||||
-behaviour(minirest_api).
|
-behaviour(minirest_api).
|
||||||
|
|
||||||
|
-include_lib("typerefl/include/types.hrl").
|
||||||
|
|
||||||
|
-define(BAD_REQUEST, 'BAD_REQUEST').
|
||||||
|
-define(NOT_FOUND, 'NOT_FOUND').
|
||||||
|
-define(INTERNAL_ERROR, 'INTERNAL_SERVER_ERROR').
|
||||||
|
|
||||||
|
-import(hoconsc, [mk/2, ref/1, ref/2]).
|
||||||
|
-import(emqx_dashboard_swagger, [error_codes/2]).
|
||||||
|
|
||||||
-import(emqx_gateway_http,
|
-import(emqx_gateway_http,
|
||||||
[ return_http_error/2
|
[ return_http_error/2
|
||||||
, schema_bad_request/0
|
|
||||||
, schema_not_found/0
|
|
||||||
, schema_internal_error/0
|
|
||||||
, schema_no_content/0
|
|
||||||
, with_gateway/2
|
, with_gateway/2
|
||||||
|
, with_listener_authn/3
|
||||||
, checks/2
|
, checks/2
|
||||||
]).
|
]).
|
||||||
|
|
||||||
-import(emqx_gateway_api_authn, [schema_authn/0]).
|
-import(emqx_gateway_api_authn, [schema_authn/0]).
|
||||||
|
|
||||||
%% minirest behaviour callbacks
|
%% minirest/dashbaord_swagger behaviour callbacks
|
||||||
-export([api_spec/0]).
|
-export([ api_spec/0
|
||||||
|
, paths/0
|
||||||
|
, schema/1
|
||||||
|
]).
|
||||||
|
|
||||||
|
-export([ roots/0
|
||||||
|
, fields/1
|
||||||
|
]).
|
||||||
|
|
||||||
%% http handlers
|
%% http handlers
|
||||||
-export([ listeners/2
|
-export([ listeners/2
|
||||||
, listeners_insta/2
|
, listeners_insta/2
|
||||||
, listeners_insta_authn/2
|
, listeners_insta_authn/2
|
||||||
|
, users/2
|
||||||
|
, users_insta/2
|
||||||
|
, import_users/2
|
||||||
]).
|
]).
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
@ -44,12 +60,15 @@
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
api_spec() ->
|
api_spec() ->
|
||||||
{metadata(apis()), []}.
|
emqx_dashboard_swagger:spec(?MODULE, #{check_schema => true}).
|
||||||
|
|
||||||
apis() ->
|
paths() ->
|
||||||
[ {"/gateway/:name/listeners", listeners}
|
[ "/gateway/:name/listeners"
|
||||||
, {"/gateway/:name/listeners/:id", listeners_insta}
|
, "/gateway/:name/listeners/:id"
|
||||||
, {"/gateway/:name/listeners/:id/authentication", listeners_insta_authn}
|
, "/gateway/:name/listeners/:id/authentication"
|
||||||
|
, "/gateway/:name/listeners/:id/authentication/users"
|
||||||
|
, "/gateway/:name/listeners/:id/authentication/users/:uid"
|
||||||
|
, "/gateway/:name/listeners/:id/authentication/import_users"
|
||||||
].
|
].
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
@ -145,126 +164,297 @@ listeners_insta_authn(delete, #{bindings := #{name := Name0,
|
||||||
{204}
|
{204}
|
||||||
end).
|
end).
|
||||||
|
|
||||||
|
users(get, #{bindings := #{name := Name0, id := Id}, query_string := Qs}) ->
|
||||||
|
with_listener_authn(Name0, Id,
|
||||||
|
fun(_GwName, #{id := AuthId, chain_name := ChainName}) ->
|
||||||
|
emqx_authn_api:list_users(ChainName, AuthId, page_pramas(Qs))
|
||||||
|
end);
|
||||||
|
users(post, #{bindings := #{name := Name0, id := Id},
|
||||||
|
body := Body}) ->
|
||||||
|
with_listener_authn(Name0, Id,
|
||||||
|
fun(_GwName, #{id := AuthId, chain_name := ChainName}) ->
|
||||||
|
emqx_authn_api:add_user(ChainName, AuthId, Body)
|
||||||
|
end).
|
||||||
|
|
||||||
|
users_insta(get, #{bindings := #{name := Name0, id := Id, uid := UserId}}) ->
|
||||||
|
with_listener_authn(Name0, Id,
|
||||||
|
fun(_GwName, #{id := AuthId, chain_name := ChainName}) ->
|
||||||
|
emqx_authn_api:find_user(ChainName, AuthId, UserId)
|
||||||
|
end);
|
||||||
|
users_insta(put, #{bindings := #{name := Name0, id := Id, uid := UserId},
|
||||||
|
body := Body}) ->
|
||||||
|
with_listener_authn(Name0, Id,
|
||||||
|
fun(_GwName, #{id := AuthId, chain_name := ChainName}) ->
|
||||||
|
emqx_authn_api:update_user(ChainName, AuthId, UserId, Body)
|
||||||
|
end);
|
||||||
|
users_insta(delete, #{bindings := #{name := Name0, id := Id, uid := UserId}}) ->
|
||||||
|
with_listener_authn(Name0, Id,
|
||||||
|
fun(_GwName, #{id := AuthId, chain_name := ChainName}) ->
|
||||||
|
emqx_authn_api:delete_user(ChainName, AuthId, UserId)
|
||||||
|
end).
|
||||||
|
|
||||||
|
import_users(post, #{bindings := #{name := Name0, id := Id},
|
||||||
|
body := Body}) ->
|
||||||
|
with_listener_authn(Name0, Id,
|
||||||
|
fun(_GwName, #{id := AuthId, chain_name := ChainName}) ->
|
||||||
|
case maps:get(<<"filename">>, Body, undefined) of
|
||||||
|
undefined ->
|
||||||
|
emqx_authn_api:serialize_error({missing_parameter, filename});
|
||||||
|
Filename ->
|
||||||
|
case emqx_authentication:import_users(
|
||||||
|
ChainName, AuthId, Filename) of
|
||||||
|
ok -> {204};
|
||||||
|
{error, Reason} ->
|
||||||
|
emqx_authn_api:serialize_error(Reason)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end).
|
||||||
|
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Utils
|
||||||
|
|
||||||
|
page_pramas(Qs) ->
|
||||||
|
maps:with([<<"page">>, <<"limit">>], Qs).
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
%% Swagger defines
|
%% Swagger defines
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
metadata(APIs) ->
|
schema("/gateway/:name/listeners") ->
|
||||||
metadata(APIs, []).
|
#{ 'operationId' => listeners,
|
||||||
metadata([], APIAcc) ->
|
get =>
|
||||||
lists:reverse(APIAcc);
|
|
||||||
metadata([{Path, Fun}|More], APIAcc) ->
|
|
||||||
Methods = [get, post, put, delete, patch],
|
|
||||||
Mds = lists:foldl(fun(M, Acc) ->
|
|
||||||
try
|
|
||||||
Acc#{M => swagger(Path, M)}
|
|
||||||
catch
|
|
||||||
error : function_clause ->
|
|
||||||
Acc
|
|
||||||
end
|
|
||||||
end, #{}, Methods),
|
|
||||||
metadata(More, [{Path, Mds, Fun} | APIAcc]).
|
|
||||||
|
|
||||||
swagger("/gateway/:name/listeners", get) ->
|
|
||||||
#{ description => <<"Get the gateway listeners">>
|
#{ description => <<"Get the gateway listeners">>
|
||||||
, parameters => params_gateway_name_in_path()
|
, parameters => params_gateway_name_in_path()
|
||||||
, responses =>
|
, responses =>
|
||||||
#{ <<"400">> => schema_bad_request()
|
#{ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>)
|
||||||
, <<"404">> => schema_not_found()
|
, 404 => error_codes([?NOT_FOUND], <<"Not Found">>)
|
||||||
, <<"500">> => schema_internal_error()
|
, 500 => error_codes([?INTERNAL_ERROR],
|
||||||
, <<"200">> => schema_listener_list()
|
<<"Ineternal Server Error">>)
|
||||||
|
, 200 => emqx_dashboard_swagger:schema_with_examples(
|
||||||
|
hoconsc:array(ref(listener)),
|
||||||
|
examples_listener_list())
|
||||||
}
|
}
|
||||||
};
|
},
|
||||||
swagger("/gateway/:name/listeners", post) ->
|
post =>
|
||||||
#{ description => <<"Create the gateway listener">>
|
#{ description => <<"Create the gateway listener">>
|
||||||
, parameters => params_gateway_name_in_path()
|
, parameters => params_gateway_name_in_path()
|
||||||
, requestBody => schema_listener()
|
, 'requestBody' => emqx_dashboard_swagger:schema_with_examples(
|
||||||
|
ref(listener),
|
||||||
|
examples_listener())
|
||||||
, responses =>
|
, responses =>
|
||||||
#{ <<"400">> => schema_bad_request()
|
#{ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>)
|
||||||
, <<"404">> => schema_not_found()
|
, 404 => error_codes([?NOT_FOUND], <<"Not Found">>)
|
||||||
, <<"500">> => schema_internal_error()
|
, 500 => error_codes([?INTERNAL_ERROR],
|
||||||
, <<"200">> => schema_listener_list()
|
<<"Ineternal Server Error">>)
|
||||||
|
, 204 => <<"Created">>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
swagger("/gateway/:name/listeners/:id", get) ->
|
schema("/gateway/:name/listeners/:id") ->
|
||||||
|
#{ 'operationId' => listeners_insta,
|
||||||
|
get =>
|
||||||
#{ description => <<"Get the gateway listener configurations">>
|
#{ description => <<"Get the gateway listener configurations">>
|
||||||
, parameters => params_gateway_name_in_path()
|
, parameters => params_gateway_name_in_path()
|
||||||
++ params_listener_id_in_path()
|
++ params_listener_id_in_path()
|
||||||
, responses =>
|
, responses =>
|
||||||
#{ <<"400">> => schema_bad_request()
|
#{ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>)
|
||||||
, <<"404">> => schema_not_found()
|
, 404 => error_codes([?NOT_FOUND], <<"Not Found">>)
|
||||||
, <<"500">> => schema_internal_error()
|
, 500 => error_codes([?INTERNAL_ERROR],
|
||||||
, <<"200">> => schema_listener()
|
<<"Ineternal Server Error">>)
|
||||||
|
, 200 => emqx_dashboard_swagger:schema_with_examples(
|
||||||
|
ref(listener),
|
||||||
|
examples_listener())
|
||||||
}
|
}
|
||||||
};
|
},
|
||||||
swagger("/gateway/:name/listeners/:id", delete) ->
|
delete =>
|
||||||
#{ description => <<"Delete the gateway listener">>
|
#{ description => <<"Delete the gateway listener">>
|
||||||
, parameters => params_gateway_name_in_path()
|
, parameters => params_gateway_name_in_path()
|
||||||
++ params_listener_id_in_path()
|
++ params_listener_id_in_path()
|
||||||
, responses =>
|
, responses =>
|
||||||
#{ <<"400">> => schema_bad_request()
|
#{ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>)
|
||||||
, <<"404">> => schema_not_found()
|
, 404 => error_codes([?NOT_FOUND], <<"Not Found">>)
|
||||||
, <<"500">> => schema_internal_error()
|
, 500 => error_codes([?INTERNAL_ERROR],
|
||||||
, <<"204">> => schema_no_content()
|
<<"Ineternal Server Error">>)
|
||||||
|
, 204 => <<"Deleted">>
|
||||||
}
|
}
|
||||||
};
|
},
|
||||||
swagger("/gateway/:name/listeners/:id", put) ->
|
put =>
|
||||||
#{ description => <<"Update the gateway listener">>
|
#{ description => <<"Update the gateway listener">>
|
||||||
, parameters => params_gateway_name_in_path()
|
, parameters => params_gateway_name_in_path()
|
||||||
++ params_listener_id_in_path()
|
++ params_listener_id_in_path()
|
||||||
, requestBody => schema_listener()
|
, 'requestBody' => emqx_dashboard_swagger:schema_with_examples(
|
||||||
|
ref(listener),
|
||||||
|
examples_listener())
|
||||||
, responses =>
|
, responses =>
|
||||||
#{ <<"400">> => schema_bad_request()
|
#{ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>)
|
||||||
, <<"404">> => schema_not_found()
|
, 404 => error_codes([?NOT_FOUND], <<"Not Found">>)
|
||||||
, <<"500">> => schema_internal_error()
|
, 500 => error_codes([?INTERNAL_ERROR],
|
||||||
, <<"200">> => schema_no_content()
|
<<"Ineternal Server Error">>)
|
||||||
|
, 200 => <<"Updated">>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
swagger("/gateway/:name/listeners/:id/authentication", get) ->
|
schema("/gateway/:name/listeners/:id/authentication") ->
|
||||||
|
#{ 'operationId' => listeners_insta_authn,
|
||||||
|
get =>
|
||||||
#{ description => <<"Get the listener's authentication info">>
|
#{ description => <<"Get the listener's authentication info">>
|
||||||
, parameters => params_gateway_name_in_path()
|
, parameters => params_gateway_name_in_path()
|
||||||
++ params_listener_id_in_path()
|
++ params_listener_id_in_path()
|
||||||
, responses =>
|
, responses =>
|
||||||
#{ <<"400">> => schema_bad_request()
|
#{ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>)
|
||||||
, <<"404">> => schema_not_found()
|
, 404 => error_codes([?NOT_FOUND], <<"Not Found">>)
|
||||||
, <<"500">> => schema_internal_error()
|
, 500 => error_codes([?INTERNAL_ERROR],
|
||||||
, <<"200">> => schema_authn()
|
<<"Ineternal Server Error">>)
|
||||||
, <<"204">> => schema_no_content()
|
, 200 => schema_authn()
|
||||||
|
, 204 => <<"Authentication does not initiated">>
|
||||||
}
|
}
|
||||||
};
|
},
|
||||||
swagger("/gateway/:name/listeners/:id/authentication", post) ->
|
post =>
|
||||||
#{ description => <<"Add authentication for the listener">>
|
#{ description => <<"Add authentication for the listener">>
|
||||||
, parameters => params_gateway_name_in_path()
|
, parameters => params_gateway_name_in_path()
|
||||||
++ params_listener_id_in_path()
|
++ params_listener_id_in_path()
|
||||||
, requestBody => schema_authn()
|
, 'requestBody' => schema_authn()
|
||||||
, responses =>
|
, responses =>
|
||||||
#{ <<"400">> => schema_bad_request()
|
#{ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>)
|
||||||
, <<"404">> => schema_not_found()
|
, 404 => error_codes([?NOT_FOUND], <<"Not Found">>)
|
||||||
, <<"500">> => schema_internal_error()
|
, 500 => error_codes([?INTERNAL_ERROR],
|
||||||
, <<"204">> => schema_no_content()
|
<<"Ineternal Server Error">>)
|
||||||
|
, 204 => <<"Added">>
|
||||||
}
|
}
|
||||||
};
|
},
|
||||||
swagger("/gateway/:name/listeners/:id/authentication", put) ->
|
put =>
|
||||||
#{ description => <<"Update authentication for the listener">>
|
#{ description => <<"Update authentication for the listener">>
|
||||||
, parameters => params_gateway_name_in_path()
|
, parameters => params_gateway_name_in_path()
|
||||||
++ params_listener_id_in_path()
|
++ params_listener_id_in_path()
|
||||||
, requestBody => schema_authn()
|
, 'requestBody' => schema_authn()
|
||||||
, responses =>
|
, responses =>
|
||||||
#{ <<"400">> => schema_bad_request()
|
#{ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>)
|
||||||
, <<"404">> => schema_not_found()
|
, 404 => error_codes([?NOT_FOUND], <<"Not Found">>)
|
||||||
, <<"500">> => schema_internal_error()
|
, 500 => error_codes([?INTERNAL_ERROR],
|
||||||
, <<"204">> => schema_no_content()
|
<<"Ineternal Server Error">>)
|
||||||
|
, 204 => <<"Updated">>
|
||||||
}
|
}
|
||||||
};
|
},
|
||||||
swagger("/gateway/:name/listeners/:id/authentication", delete) ->
|
delete =>
|
||||||
#{ description => <<"Remove authentication for the listener">>
|
#{ description => <<"Remove authentication for the listener">>
|
||||||
, parameters => params_gateway_name_in_path()
|
, parameters => params_gateway_name_in_path()
|
||||||
++ params_listener_id_in_path()
|
++ params_listener_id_in_path()
|
||||||
, responses =>
|
, responses =>
|
||||||
#{ <<"400">> => schema_bad_request()
|
#{ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>)
|
||||||
, <<"404">> => schema_not_found()
|
, 404 => error_codes([?NOT_FOUND], <<"Not Found">>)
|
||||||
, <<"500">> => schema_internal_error()
|
, 500 => error_codes([?INTERNAL_ERROR],
|
||||||
, <<"204">> => schema_no_content()
|
<<"Ineternal Server Error">>)
|
||||||
|
, 204 => <<"Deleted">>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
schema("/gateway/:name/listeners/:id/authentication/users") ->
|
||||||
|
#{ 'operationId' => users
|
||||||
|
, get =>
|
||||||
|
#{ description => <<"Get the users for the authentication">>
|
||||||
|
, parameters => params_gateway_name_in_path() ++
|
||||||
|
params_listener_id_in_path() ++
|
||||||
|
params_paging_in_qs()
|
||||||
|
, responses =>
|
||||||
|
#{ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>)
|
||||||
|
, 404 => error_codes([?NOT_FOUND], <<"Not Found">>)
|
||||||
|
, 500 => error_codes([?INTERNAL_ERROR],
|
||||||
|
<<"Ineternal Server Error">>)
|
||||||
|
, 200 => emqx_dashboard_swagger:schema_with_example(
|
||||||
|
ref(emqx_authn_api, response_user),
|
||||||
|
emqx_authn_api:response_user_examples())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
post =>
|
||||||
|
#{ description => <<"Add user for the authentication">>
|
||||||
|
, parameters => params_gateway_name_in_path() ++
|
||||||
|
params_listener_id_in_path()
|
||||||
|
, 'requestBody' => emqx_dashboard_swagger:schema_with_examples(
|
||||||
|
ref(emqx_authn_api, request_user_create),
|
||||||
|
emqx_authn_api:request_user_create_examples())
|
||||||
|
, responses =>
|
||||||
|
#{ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>)
|
||||||
|
, 404 => error_codes([?NOT_FOUND], <<"Not Found">>)
|
||||||
|
, 500 => error_codes([?INTERNAL_ERROR],
|
||||||
|
<<"Ineternal Server Error">>)
|
||||||
|
, 201 => emqx_dashboard_swagger:schema_with_example(
|
||||||
|
ref(emqx_authn_api, response_user),
|
||||||
|
emqx_authn_api:response_user_examples())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
schema("/gateway/:name/listeners/:id/authentication/users/:uid") ->
|
||||||
|
#{ 'operationId' => users_insta
|
||||||
|
, get =>
|
||||||
|
#{ description => <<"Get user info from the gateway "
|
||||||
|
"authentication">>
|
||||||
|
, parameters => params_gateway_name_in_path() ++
|
||||||
|
params_listener_id_in_path() ++
|
||||||
|
params_userid_in_path()
|
||||||
|
, responses =>
|
||||||
|
#{ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>)
|
||||||
|
, 404 => error_codes([?NOT_FOUND], <<"Not Found">>)
|
||||||
|
, 500 => error_codes([?INTERNAL_ERROR],
|
||||||
|
<<"Ineternal Server Error">>)
|
||||||
|
, 200 => emqx_dashboard_swagger:schema_with_example(
|
||||||
|
ref(emqx_authn_api, response_user),
|
||||||
|
emqx_authn_api:response_user_examples())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
put =>
|
||||||
|
#{ description => <<"Update the user info for the gateway "
|
||||||
|
"authentication">>
|
||||||
|
, parameters => params_gateway_name_in_path() ++
|
||||||
|
params_listener_id_in_path() ++
|
||||||
|
params_userid_in_path()
|
||||||
|
, 'requestBody' => emqx_dashboard_swagger:schema_with_examples(
|
||||||
|
ref(emqx_authn_api, request_user_update),
|
||||||
|
emqx_authn_api:request_user_update_examples())
|
||||||
|
, responses =>
|
||||||
|
#{ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>)
|
||||||
|
, 404 => error_codes([?NOT_FOUND], <<"Not Found">>)
|
||||||
|
, 500 => error_codes([?INTERNAL_ERROR],
|
||||||
|
<<"Ineternal Server Error">>)
|
||||||
|
, 200 => emqx_dashboard_swagger:schema_with_example(
|
||||||
|
ref(emqx_authn_api, response_user),
|
||||||
|
emqx_authn_api:response_user_examples())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
delete =>
|
||||||
|
#{ description => <<"Delete the user for the gateway "
|
||||||
|
"authentication">>
|
||||||
|
, parameters => params_gateway_name_in_path() ++
|
||||||
|
params_listener_id_in_path() ++
|
||||||
|
params_userid_in_path()
|
||||||
|
, responses =>
|
||||||
|
#{ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>)
|
||||||
|
, 404 => error_codes([?NOT_FOUND], <<"Not Found">>)
|
||||||
|
, 500 => error_codes([?INTERNAL_ERROR],
|
||||||
|
<<"Ineternal Server Error">>)
|
||||||
|
, 200 => emqx_dashboard_swagger:schema_with_example(
|
||||||
|
ref(emqx_authn_api, response_user),
|
||||||
|
emqx_authn_api:response_user_examples())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
schema("/gateway/:name/listeners/:id/authentication/import_users") ->
|
||||||
|
#{ 'operationId' => import_users
|
||||||
|
, post =>
|
||||||
|
#{ description => <<"Import users into the gateway authentication">>
|
||||||
|
, parameters => params_gateway_name_in_path() ++
|
||||||
|
params_listener_id_in_path()
|
||||||
|
, 'requestBody' => emqx_dashboard_swagger:schema_with_examples(
|
||||||
|
ref(emqx_authn_api, request_import_users),
|
||||||
|
emqx_authn_api:request_import_users_examples()
|
||||||
|
)
|
||||||
|
, responses =>
|
||||||
|
#{ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>)
|
||||||
|
, 404 => error_codes([?NOT_FOUND], <<"Not Found">>)
|
||||||
|
, 500 => error_codes([?INTERNAL_ERROR],
|
||||||
|
<<"Ineternal Server Error">>)
|
||||||
|
%% XXX: Put a hint message into 204 return ?
|
||||||
|
, 204 => <<"Imported">>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}.
|
}.
|
||||||
|
|
||||||
|
@ -272,120 +462,183 @@ swagger("/gateway/:name/listeners/:id/authentication", delete) ->
|
||||||
%% params defines
|
%% params defines
|
||||||
|
|
||||||
params_gateway_name_in_path() ->
|
params_gateway_name_in_path() ->
|
||||||
[#{ name => name
|
[{name,
|
||||||
, in => path
|
mk(binary(),
|
||||||
, schema => #{type => string}
|
#{ in => path
|
||||||
, required => true
|
, desc => <<"Gateway Name">>
|
||||||
}].
|
})}
|
||||||
|
].
|
||||||
|
|
||||||
params_listener_id_in_path() ->
|
params_listener_id_in_path() ->
|
||||||
[#{ name => id
|
[{id,
|
||||||
, in => path
|
mk(binary(),
|
||||||
, schema => #{type => string}
|
#{ in => path
|
||||||
, required => true
|
, desc => <<"Listener ID">>
|
||||||
}].
|
})}
|
||||||
|
].
|
||||||
|
|
||||||
|
params_userid_in_path() ->
|
||||||
|
[{uid, mk(binary(),
|
||||||
|
#{ in => path
|
||||||
|
, desc => <<"User ID">>
|
||||||
|
})}
|
||||||
|
].
|
||||||
|
|
||||||
|
params_paging_in_qs() ->
|
||||||
|
[{page, mk(integer(),
|
||||||
|
#{ in => query
|
||||||
|
, nullable => true
|
||||||
|
, desc => <<"Page Index">>
|
||||||
|
})},
|
||||||
|
{limit, mk(integer(),
|
||||||
|
#{ in => query
|
||||||
|
, nullable => true
|
||||||
|
, desc => <<"Page Limit">>
|
||||||
|
})}
|
||||||
|
].
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
%% schemas
|
%% schemas
|
||||||
|
|
||||||
schema_listener_list() ->
|
roots() ->
|
||||||
emqx_mgmt_util:array_schema(
|
[ listener
|
||||||
#{ type => object
|
|
||||||
, properties => properties_listener()
|
|
||||||
},
|
|
||||||
<<"Listener list">>
|
|
||||||
).
|
|
||||||
|
|
||||||
schema_listener() ->
|
|
||||||
emqx_mgmt_util:schema(
|
|
||||||
#{ type => object
|
|
||||||
, properties => properties_listener()
|
|
||||||
}
|
|
||||||
).
|
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
%% properties
|
|
||||||
|
|
||||||
properties_listener() ->
|
|
||||||
emqx_mgmt_util:properties(
|
|
||||||
raw_properties_common_listener() ++
|
|
||||||
[ {tcp, object, raw_properties_tcp_opts()}
|
|
||||||
, {ssl, object, raw_properties_ssl_opts()}
|
|
||||||
, {udp, object, raw_properties_udp_opts()}
|
|
||||||
, {dtls, object, raw_properties_dtls_opts()}
|
|
||||||
]).
|
|
||||||
|
|
||||||
raw_properties_tcp_opts() ->
|
|
||||||
[ {active_n, integer, <<>>}
|
|
||||||
, {backlog, integer, <<>>}
|
|
||||||
, {buffer, string, <<>>}
|
|
||||||
, {recbuf, string, <<>>}
|
|
||||||
, {sndbuf, string, <<>>}
|
|
||||||
, {high_watermark, string, <<>>}
|
|
||||||
, {nodelay, boolean, <<>>}
|
|
||||||
, {reuseaddr, boolean, <<>>}
|
|
||||||
, {send_timeout, string, <<>>}
|
|
||||||
, {send_timeout_close, boolean, <<>>}
|
|
||||||
].
|
].
|
||||||
|
|
||||||
raw_properties_ssl_opts() ->
|
fields(listener) ->
|
||||||
[ {cacertfile, string, <<>>}
|
common_listener_opts() ++
|
||||||
, {certfile, string, <<>>}
|
[ {tcp,
|
||||||
, {keyfile, string, <<>>}
|
mk(ref(tcp_listener_opts),
|
||||||
, {verify, string, <<>>}
|
#{ nullable => {true, recursively}
|
||||||
, {fail_if_no_peer_cert, boolean, <<>>}
|
, desc => <<"The tcp socket options for tcp or ssl listener">>
|
||||||
, {server_name_indication, boolean, <<>>}
|
})}
|
||||||
, {depth, integer, <<>>}
|
, {ssl,
|
||||||
, {password, string, <<>>}
|
mk(ref(ssl_listener_opts),
|
||||||
, {handshake_timeout, string, <<>>}
|
#{ nullable => {true, recursively}
|
||||||
, {versions, {array, string}, <<>>}
|
, desc => <<"The ssl socket options for ssl listener">>
|
||||||
, {ciphers, {array, string}, <<>>}
|
})}
|
||||||
, {user_lookup_fun, string, <<>>}
|
, {udp,
|
||||||
, {reuse_sessions, boolean, <<>>}
|
mk(ref(udp_listener_opts),
|
||||||
, {secure_renegotiate, boolean, <<>>}
|
#{ nullable => {true, recursively}
|
||||||
, {honor_cipher_order, boolean, <<>>}
|
, desc => <<"The udp socket options for udp or dtls listener">>
|
||||||
, {dhfile, string, <<>>}
|
})}
|
||||||
].
|
, {dtls,
|
||||||
|
mk(ref(dtls_listener_opts),
|
||||||
raw_properties_udp_opts() ->
|
#{ nullable => {true, recursively}
|
||||||
[ {active_n, integer, <<>>}
|
, desc => <<"The dtls socket options for dtls listener">>
|
||||||
, {buffer, string, <<>>}
|
})}
|
||||||
, {recbuf, string, <<>>}
|
];
|
||||||
, {sndbuf, string, <<>>}
|
fields(tcp_listener_opts) ->
|
||||||
, {reuseaddr, boolean, <<>>}
|
[ {active_n, mk(integer(), #{})}
|
||||||
].
|
, {backlog, mk(integer(), #{})}
|
||||||
|
, {buffer, mk(binary(), #{})}
|
||||||
raw_properties_dtls_opts() ->
|
, {recbuf, mk(binary(), #{})}
|
||||||
|
, {sndbuf, mk(binary(), #{})}
|
||||||
|
, {high_watermark, mk(binary(), #{})}
|
||||||
|
, {nodelay, mk(boolean(), #{})}
|
||||||
|
, {reuseaddr, boolean()}
|
||||||
|
, {send_timeout, binary()}
|
||||||
|
, {send_timeout_close, boolean()}
|
||||||
|
];
|
||||||
|
fields(ssl_listener_opts) ->
|
||||||
|
[ {cacertfile, binary()}
|
||||||
|
, {certfile, binary()}
|
||||||
|
, {keyfile, binary()}
|
||||||
|
, {verify, binary()}
|
||||||
|
, {fail_if_no_peer_cert, boolean()}
|
||||||
|
, {server_name_indication, boolean()}
|
||||||
|
, {depth, integer()}
|
||||||
|
, {password, binary()}
|
||||||
|
, {handshake_timeout, binary()}
|
||||||
|
, {versions, hoconsc:array(binary())}
|
||||||
|
, {ciphers, hoconsc:array(binary())}
|
||||||
|
, {user_lookup_fun, binary()}
|
||||||
|
, {reuse_sessions, boolean()}
|
||||||
|
, {secure_renegotiate, boolean()}
|
||||||
|
, {honor_cipher_order, boolean()}
|
||||||
|
, {dhfile, binary()}
|
||||||
|
];
|
||||||
|
fields(udp_listener_opts) ->
|
||||||
|
[ {active_n, integer()}
|
||||||
|
, {buffer, binary()}
|
||||||
|
, {recbuf, binary()}
|
||||||
|
, {sndbuf, binary()}
|
||||||
|
, {reuseaddr, boolean()}
|
||||||
|
];
|
||||||
|
fields(dtls_listener_opts) ->
|
||||||
Ls = lists_key_without(
|
Ls = lists_key_without(
|
||||||
[versions,ciphers,handshake_timeout], 1,
|
[versions,ciphers,handshake_timeout], 1,
|
||||||
raw_properties_ssl_opts()
|
fields(ssl_listener_opts)
|
||||||
),
|
),
|
||||||
[ {versions, {array, string}, <<>>}
|
[ {versions, hoconsc:array(binary())}
|
||||||
, {ciphers, {array, string}, <<>>}
|
, {ciphers, hoconsc:array(binary())}
|
||||||
| Ls].
|
| Ls].
|
||||||
|
|
||||||
lists_key_without([], _N, L) ->
|
lists_key_without([], _N, L) ->
|
||||||
L;
|
L;
|
||||||
lists_key_without([K|Ks], N, L) ->
|
lists_key_without([K | Ks], N, L) ->
|
||||||
lists_key_without(Ks, N, lists:keydelete(K, N, L)).
|
lists_key_without(Ks, N, lists:keydelete(K, N, L)).
|
||||||
|
|
||||||
raw_properties_common_listener() ->
|
common_listener_opts() ->
|
||||||
[ {enable, boolean, <<"Whether to enable this listener">>}
|
[ {enable,
|
||||||
, {id, string, <<"Listener Id">>}
|
mk(boolean(),
|
||||||
, {name, string, <<"Listener name">>}
|
#{ nullable => true
|
||||||
, {type, string,
|
, desc => <<"Whether to enable this listener">>})}
|
||||||
<<"Listener type. Enum: tcp, udp, ssl, dtls">>,
|
, {id,
|
||||||
[<<"tcp">>, <<"ssl">>, <<"udp">>, <<"dtls">>]}
|
mk(binary(),
|
||||||
, {running, boolean, <<"Listener running status">>}
|
#{ nullable => true
|
||||||
, {bind, string, <<"Listener bind address or port">>}
|
, desc => <<"Listener Id">>})}
|
||||||
, {acceptors, integer, <<"Listener acceptors number">>}
|
, {name,
|
||||||
, {access_rules, {array, string}, <<"Listener Access rules for client">>}
|
mk(binary(),
|
||||||
, {max_conn_rate, integer, <<"Max connection rate for the listener">>}
|
#{ nullable => true
|
||||||
, {max_connections, integer, <<"Max connections for the listener">>}
|
, desc => <<"Listener name">>})}
|
||||||
, {mountpoint, string,
|
, {type,
|
||||||
<<"The Mounpoint for clients of the listener. "
|
mk(hoconsc:enum([tcp, ssl, udp, dtls]),
|
||||||
|
#{ nullable => true
|
||||||
|
, desc => <<"Listener type. Enum: tcp, udp, ssl, dtls">>})}
|
||||||
|
, {running,
|
||||||
|
mk(boolean(),
|
||||||
|
#{ nullable => true
|
||||||
|
, desc => <<"Listener running status">>})}
|
||||||
|
, {bind,
|
||||||
|
mk(binary(),
|
||||||
|
#{ nullable => true
|
||||||
|
, desc => <<"Listener bind address or port">>})}
|
||||||
|
, {acceptors,
|
||||||
|
mk(integer(),
|
||||||
|
#{ nullable => true
|
||||||
|
, desc => <<"Listener acceptors number">>})}
|
||||||
|
, {access_rules,
|
||||||
|
mk(hoconsc:array(binary()),
|
||||||
|
#{ nullable => true
|
||||||
|
, desc => <<"Listener Access rules for client">>})}
|
||||||
|
, {max_conn_rate,
|
||||||
|
mk(integer(),
|
||||||
|
#{ nullable => true
|
||||||
|
, desc => <<"Max connection rate for the listener">>})}
|
||||||
|
, {max_connections,
|
||||||
|
mk(integer(),
|
||||||
|
#{ nullable => true
|
||||||
|
, desc => <<"Max connections for the listener">>})}
|
||||||
|
, {mountpoint,
|
||||||
|
mk(binary(),
|
||||||
|
#{ nullable => true
|
||||||
|
, desc =>
|
||||||
|
<<"The Mounpoint for clients of the listener. "
|
||||||
"The gateway-level mountpoint configuration can be overloaded "
|
"The gateway-level mountpoint configuration can be overloaded "
|
||||||
"when it is not null or empty string">>}
|
"when it is not null or empty string">>})}
|
||||||
%% FIXME:
|
%% FIXME:
|
||||||
, {authentication, string, <<"NOT-SUPPORTED-NOW">>}
|
, {authentication,
|
||||||
|
mk(emqx_authn_schema:authenticator_type(),
|
||||||
|
#{ nullable => {true, recursively}
|
||||||
|
, desc => <<"The authenticatior for this listener">>
|
||||||
|
})}
|
||||||
].
|
].
|
||||||
|
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% examples
|
||||||
|
|
||||||
|
examples_listener_list() ->
|
||||||
|
#{stomp_listeners => [examples_listener()]}.
|
||||||
|
|
||||||
|
examples_listener() ->
|
||||||
|
#{}.
|
||||||
|
|
|
@ -53,6 +53,8 @@
|
||||||
%% Utils for http, swagger, etc.
|
%% Utils for http, swagger, etc.
|
||||||
-export([ return_http_error/2
|
-export([ return_http_error/2
|
||||||
, with_gateway/2
|
, with_gateway/2
|
||||||
|
, with_authn/2
|
||||||
|
, with_listener_authn/3
|
||||||
, checks/2
|
, checks/2
|
||||||
, schema_bad_request/0
|
, schema_bad_request/0
|
||||||
, schema_not_found/0
|
, schema_not_found/0
|
||||||
|
@ -69,6 +71,10 @@
|
||||||
, listeners => []
|
, listeners => []
|
||||||
}.
|
}.
|
||||||
|
|
||||||
|
-elvis([{elvis_style, god_modules, disable}]).
|
||||||
|
-elvis([{elvis_style, no_nested_try_catch, disable}]).
|
||||||
|
|
||||||
|
|
||||||
-define(DEFAULT_CALL_TIMEOUT, 15000).
|
-define(DEFAULT_CALL_TIMEOUT, 15000).
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
@ -159,14 +165,31 @@ remove_listener(ListenerId) ->
|
||||||
|
|
||||||
-spec authn(gateway_name()) -> map().
|
-spec authn(gateway_name()) -> map().
|
||||||
authn(GwName) ->
|
authn(GwName) ->
|
||||||
|
%% XXX: Need append chain-nanme, authenticator-id?
|
||||||
Path = [gateway, GwName, authentication],
|
Path = [gateway, GwName, authentication],
|
||||||
emqx_map_lib:jsonable_map(emqx:get_config(Path)).
|
ChainName = emqx_gateway_utils:global_chain(GwName),
|
||||||
|
wrap_chain_name(
|
||||||
|
ChainName,
|
||||||
|
emqx_map_lib:jsonable_map(emqx:get_config(Path))
|
||||||
|
).
|
||||||
|
|
||||||
-spec authn(gateway_name(), binary()) -> map().
|
-spec authn(gateway_name(), binary()) -> map().
|
||||||
authn(GwName, ListenerId) ->
|
authn(GwName, ListenerId) ->
|
||||||
{_, Type, Name} = emqx_gateway_utils:parse_listener_id(ListenerId),
|
{_, Type, Name} = emqx_gateway_utils:parse_listener_id(ListenerId),
|
||||||
Path = [gateway, GwName, listeners, Type, Name, authentication],
|
Path = [gateway, GwName, listeners, Type, Name, authentication],
|
||||||
emqx_map_lib:jsonable_map(emqx:get_config(Path)).
|
ChainName = emqx_gateway_utils:listener_chain(GwName, Type, Name),
|
||||||
|
wrap_chain_name(
|
||||||
|
ChainName,
|
||||||
|
emqx_map_lib:jsonable_map(emqx:get_config(Path))
|
||||||
|
).
|
||||||
|
|
||||||
|
wrap_chain_name(ChainName, Conf) ->
|
||||||
|
case emqx_authentication:list_authenticators(ChainName) of
|
||||||
|
{ok, [#{id := Id} | _]} ->
|
||||||
|
Conf#{chain_name => ChainName, id => Id};
|
||||||
|
_ ->
|
||||||
|
Conf
|
||||||
|
end.
|
||||||
|
|
||||||
-spec add_authn(gateway_name(), map()) -> ok.
|
-spec add_authn(gateway_name(), map()) -> ok.
|
||||||
add_authn(GwName, AuthConf) ->
|
add_authn(GwName, AuthConf) ->
|
||||||
|
@ -303,6 +326,20 @@ codestr(401) -> 'NOT_SUPPORTED_NOW';
|
||||||
codestr(404) -> 'RESOURCE_NOT_FOUND';
|
codestr(404) -> 'RESOURCE_NOT_FOUND';
|
||||||
codestr(500) -> 'UNKNOW_ERROR'.
|
codestr(500) -> 'UNKNOW_ERROR'.
|
||||||
|
|
||||||
|
-spec with_authn(binary(), function()) -> any().
|
||||||
|
with_authn(GwName0, Fun) ->
|
||||||
|
with_gateway(GwName0, fun(GwName, _GwConf) ->
|
||||||
|
Authn = emqx_gateway_http:authn(GwName),
|
||||||
|
Fun(GwName, Authn)
|
||||||
|
end).
|
||||||
|
|
||||||
|
-spec with_listener_authn(binary(), binary(), function()) -> any().
|
||||||
|
with_listener_authn(GwName0, Id, Fun) ->
|
||||||
|
with_gateway(GwName0, fun(GwName, _GwConf) ->
|
||||||
|
Authn = emqx_gateway_http:authn(GwName, Id),
|
||||||
|
Fun(GwName, Authn)
|
||||||
|
end).
|
||||||
|
|
||||||
-spec with_gateway(binary(), function()) -> any().
|
-spec with_gateway(binary(), function()) -> any().
|
||||||
with_gateway(GwName0, Fun) ->
|
with_gateway(GwName0, Fun) ->
|
||||||
try
|
try
|
||||||
|
@ -346,7 +383,7 @@ with_gateway(GwName0, Fun) ->
|
||||||
-spec checks(list(), map()) -> ok.
|
-spec checks(list(), map()) -> ok.
|
||||||
checks([], _) ->
|
checks([], _) ->
|
||||||
ok;
|
ok;
|
||||||
checks([K|Ks], Map) ->
|
checks([K | Ks], Map) ->
|
||||||
case maps:is_key(K, Map) of
|
case maps:is_key(K, Map) of
|
||||||
true -> checks(Ks, Map);
|
true -> checks(Ks, Map);
|
||||||
false ->
|
false ->
|
||||||
|
|
|
@ -52,6 +52,8 @@
|
||||||
stopped_at :: integer() | undefined
|
stopped_at :: integer() | undefined
|
||||||
}).
|
}).
|
||||||
|
|
||||||
|
-elvis([{elvis_style, invalid_dynamic_call, disable}]).
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
%% APIs
|
%% APIs
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
@ -219,23 +221,6 @@ detailed_gateway_info(State) ->
|
||||||
%% Internal funcs
|
%% Internal funcs
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
%% same with emqx_authentication:global_chain/1
|
|
||||||
global_chain(mqtt) ->
|
|
||||||
'mqtt:global';
|
|
||||||
global_chain('mqtt-sn') ->
|
|
||||||
'mqtt-sn:global';
|
|
||||||
global_chain(coap) ->
|
|
||||||
'coap:global';
|
|
||||||
global_chain(lwm2m) ->
|
|
||||||
'lwm2m:global';
|
|
||||||
global_chain(stomp) ->
|
|
||||||
'stomp:global';
|
|
||||||
global_chain(_) ->
|
|
||||||
'unknown:global'.
|
|
||||||
|
|
||||||
listener_chain(GwName, Type, LisName) ->
|
|
||||||
emqx_gateway_utils:listener_id(GwName, Type, LisName).
|
|
||||||
|
|
||||||
%% There are two layer authentication configs
|
%% There are two layer authentication configs
|
||||||
%% stomp.authn
|
%% stomp.authn
|
||||||
%% / \
|
%% / \
|
||||||
|
@ -254,22 +239,23 @@ init_authn(GwName, Config) ->
|
||||||
|
|
||||||
do_init_authn([], Names) ->
|
do_init_authn([], Names) ->
|
||||||
Names;
|
Names;
|
||||||
do_init_authn([{_ChainName, _AuthConf = #{enable := false}}|More], Names) ->
|
do_init_authn([{_ChainName, _AuthConf = #{enable := false}} | More], Names) ->
|
||||||
do_init_authn(More, Names);
|
do_init_authn(More, Names);
|
||||||
do_init_authn([{ChainName, AuthConf}|More], Names) when is_map(AuthConf) ->
|
do_init_authn([{ChainName, AuthConf} | More], Names) when is_map(AuthConf) ->
|
||||||
_ = application:ensure_all_started(emqx_authn),
|
_ = application:ensure_all_started(emqx_authn),
|
||||||
do_create_authn_chain(ChainName, AuthConf),
|
do_create_authn_chain(ChainName, AuthConf),
|
||||||
do_init_authn(More, [ChainName|Names]);
|
do_init_authn(More, [ChainName | Names]);
|
||||||
do_init_authn([_BadConf|More], Names) ->
|
do_init_authn([_BadConf | More], Names) ->
|
||||||
do_init_authn(More, Names).
|
do_init_authn(More, Names).
|
||||||
|
|
||||||
authns(GwName, Config) ->
|
authns(GwName, Config) ->
|
||||||
Listeners = maps:to_list(maps:get(listeners, Config, #{})),
|
Listeners = maps:to_list(maps:get(listeners, Config, #{})),
|
||||||
lists:append(
|
lists:append(
|
||||||
[ [{listener_chain(GwName, LisType, LisName), authn_conf(Opts)}
|
[ [{emqx_gateway_utils:listener_chain(GwName, LisType, LisName),
|
||||||
|
authn_conf(Opts)}
|
||||||
|| {LisName, Opts} <- maps:to_list(LisNames) ]
|
|| {LisName, Opts} <- maps:to_list(LisNames) ]
|
||||||
|| {LisType, LisNames} <- Listeners])
|
|| {LisType, LisNames} <- Listeners])
|
||||||
++ [{global_chain(GwName), authn_conf(Config)}].
|
++ [{emqx_gateway_utils:global_chain(GwName), authn_conf(Config)}].
|
||||||
|
|
||||||
authn_conf(Conf) ->
|
authn_conf(Conf) ->
|
||||||
maps:get(authentication, Conf, #{enable => false}).
|
maps:get(authentication, Conf, #{enable => false}).
|
||||||
|
@ -328,13 +314,13 @@ do_update_one_by_one(NCfg, State = #state{
|
||||||
OAuths = authns(GwName, OCfg),
|
OAuths = authns(GwName, OCfg),
|
||||||
NAuths = authns(GwName, NCfg),
|
NAuths = authns(GwName, NCfg),
|
||||||
|
|
||||||
if
|
case {Status, NEnable} of
|
||||||
Status == stopped, NEnable == true ->
|
{stopped, true} ->
|
||||||
NState = State#state{config = NCfg},
|
NState = State#state{config = NCfg},
|
||||||
cb_gateway_load(NState);
|
cb_gateway_load(NState);
|
||||||
Status == stopped, NEnable == false ->
|
{stopped, false} ->
|
||||||
{ok, State#state{config = NCfg}};
|
{ok, State#state{config = NCfg}};
|
||||||
Status == running, NEnable == true ->
|
{running, true} ->
|
||||||
NState = case NAuths == OAuths of
|
NState = case NAuths == OAuths of
|
||||||
true -> State;
|
true -> State;
|
||||||
false ->
|
false ->
|
||||||
|
@ -345,12 +331,12 @@ do_update_one_by_one(NCfg, State = #state{
|
||||||
end,
|
end,
|
||||||
%% XXX: minimum impact update ???
|
%% XXX: minimum impact update ???
|
||||||
cb_gateway_update(NCfg, NState);
|
cb_gateway_update(NCfg, NState);
|
||||||
Status == running, NEnable == false ->
|
{running, false} ->
|
||||||
case cb_gateway_unload(State) of
|
case cb_gateway_unload(State) of
|
||||||
{ok, NState} -> {ok, NState#state{config = NCfg}};
|
{ok, NState} -> {ok, NState#state{config = NCfg}};
|
||||||
{error, Reason} -> {error, Reason}
|
{error, Reason} -> {error, Reason}
|
||||||
end;
|
end;
|
||||||
true ->
|
_ ->
|
||||||
throw(nomatch)
|
throw(nomatch)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
@ -448,7 +434,7 @@ cb_gateway_update(Config,
|
||||||
end.
|
end.
|
||||||
|
|
||||||
start_child_process([]) -> [];
|
start_child_process([]) -> [];
|
||||||
start_child_process([Indictor|_] = ChildPidOrSpecs) ->
|
start_child_process([Indictor | _] = ChildPidOrSpecs) ->
|
||||||
case erlang:is_pid(Indictor) of
|
case erlang:is_pid(Indictor) of
|
||||||
true ->
|
true ->
|
||||||
ChildPidOrSpecs;
|
ChildPidOrSpecs;
|
||||||
|
|
|
@ -34,6 +34,8 @@
|
||||||
, listener_id/3
|
, listener_id/3
|
||||||
, parse_listener_id/1
|
, parse_listener_id/1
|
||||||
, is_running/2
|
, is_running/2
|
||||||
|
, global_chain/1
|
||||||
|
, listener_chain/3
|
||||||
]).
|
]).
|
||||||
|
|
||||||
-export([ stringfy/1
|
-export([ stringfy/1
|
||||||
|
@ -64,6 +66,8 @@
|
||||||
-define(DEFAULT_OOM_POLICY, #{max_heap_size => 4194304,
|
-define(DEFAULT_OOM_POLICY, #{max_heap_size => 4194304,
|
||||||
message_queue_len => 32000}).
|
message_queue_len => 32000}).
|
||||||
|
|
||||||
|
-elvis([{elvis_style, god_modules, disable}]).
|
||||||
|
|
||||||
-spec childspec(supervisor:worker(), Mod :: atom())
|
-spec childspec(supervisor:worker(), Mod :: atom())
|
||||||
-> supervisor:child_spec().
|
-> supervisor:child_spec().
|
||||||
childspec(Type, Mod) ->
|
childspec(Type, Mod) ->
|
||||||
|
@ -159,6 +163,23 @@ is_running(ListenerId, #{<<"bind">> := ListenOn0}) ->
|
||||||
false
|
false
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
%% same with emqx_authentication:global_chain/1
|
||||||
|
global_chain(mqtt) ->
|
||||||
|
'mqtt:global';
|
||||||
|
global_chain('mqtt-sn') ->
|
||||||
|
'mqtt-sn:global';
|
||||||
|
global_chain(coap) ->
|
||||||
|
'coap:global';
|
||||||
|
global_chain(lwm2m) ->
|
||||||
|
'lwm2m:global';
|
||||||
|
global_chain(stomp) ->
|
||||||
|
'stomp:global';
|
||||||
|
global_chain(_) ->
|
||||||
|
'unknown:global'.
|
||||||
|
|
||||||
|
listener_chain(GwName, Type, LisName) ->
|
||||||
|
listener_id(GwName, Type, LisName).
|
||||||
|
|
||||||
bin(A) when is_atom(A) ->
|
bin(A) when is_atom(A) ->
|
||||||
atom_to_binary(A);
|
atom_to_binary(A);
|
||||||
bin(L) when is_list(L); is_binary(L) ->
|
bin(L) when is_list(L); is_binary(L) ->
|
||||||
|
@ -183,7 +204,7 @@ stringfy(T) when is_list(T); is_binary(T) ->
|
||||||
stringfy(T) ->
|
stringfy(T) ->
|
||||||
iolist_to_binary(io_lib:format("~0p", [T])).
|
iolist_to_binary(io_lib:format("~0p", [T])).
|
||||||
|
|
||||||
-spec parse_address(binary()|list()) -> {list(), integer()}.
|
-spec parse_address(binary() | list()) -> {list(), integer()}.
|
||||||
parse_address(S) when is_binary(S); is_list(S) ->
|
parse_address(S) when is_binary(S); is_list(S) ->
|
||||||
S1 = case is_binary(S) of
|
S1 = case is_binary(S) of
|
||||||
true -> lists:reverse(binary_to_list(S));
|
true -> lists:reverse(binary_to_list(S));
|
||||||
|
@ -215,9 +236,9 @@ normalize_config(RawConf) ->
|
||||||
[bind, tcp, ssl, udp, dtls]
|
[bind, tcp, ssl, udp, dtls]
|
||||||
++ proplists:get_keys(SocketOpts), Confs),
|
++ proplists:get_keys(SocketOpts), Confs),
|
||||||
Cfg = maps:merge(Cfg0, RemainCfgs),
|
Cfg = maps:merge(Cfg0, RemainCfgs),
|
||||||
[{Type, Name, ListenOn, SocketOpts, Cfg}|AccIn2]
|
[{Type, Name, ListenOn, SocketOpts, Cfg} | AccIn2]
|
||||||
end, [], Liss),
|
end, [], Liss),
|
||||||
[Listeners|AccIn1]
|
[Listeners | AccIn1]
|
||||||
end, [], LisMap)).
|
end, [], LisMap)).
|
||||||
|
|
||||||
esockd_opts(Type, Opts0) ->
|
esockd_opts(Type, Opts0) ->
|
||||||
|
|
|
@ -207,6 +207,50 @@ t_authn(_) ->
|
||||||
{204, _} = request(get, "/gateway/stomp/authentication"),
|
{204, _} = request(get, "/gateway/stomp/authentication"),
|
||||||
{204, _} = request(delete, "/gateway/stomp").
|
{204, _} = request(delete, "/gateway/stomp").
|
||||||
|
|
||||||
|
t_authn_data_mgmt(_) ->
|
||||||
|
GwConf = #{name => <<"stomp">>},
|
||||||
|
{204, _} = request(post, "/gateway", GwConf),
|
||||||
|
{204, _} = request(get, "/gateway/stomp/authentication"),
|
||||||
|
|
||||||
|
AuthConf = #{mechanism => <<"password-based">>,
|
||||||
|
backend => <<"built-in-database">>,
|
||||||
|
user_id_type => <<"clientid">>
|
||||||
|
},
|
||||||
|
{204, _} = request(post, "/gateway/stomp/authentication", AuthConf),
|
||||||
|
{200, ConfResp} = request(get, "/gateway/stomp/authentication"),
|
||||||
|
assert_confs(AuthConf, ConfResp),
|
||||||
|
|
||||||
|
User1 = #{ user_id => <<"test">>
|
||||||
|
, password => <<"123456">>
|
||||||
|
, is_superuser => false
|
||||||
|
},
|
||||||
|
{201, _} = request(post, "/gateway/stomp/authentication/users", User1),
|
||||||
|
{200, #{data := [UserRespd1]}} = request(get, "/gateway/stomp/authentication/users"),
|
||||||
|
assert_confs(UserRespd1, User1),
|
||||||
|
|
||||||
|
{200, UserRespd2} = request(get,
|
||||||
|
"/gateway/stomp/authentication/users/test"),
|
||||||
|
assert_confs(UserRespd2, User1),
|
||||||
|
|
||||||
|
{200, UserRespd3} = request(put,
|
||||||
|
"/gateway/stomp/authentication/users/test",
|
||||||
|
#{password => <<"654321">>,
|
||||||
|
is_superuser => true}),
|
||||||
|
assert_confs(UserRespd3, User1#{is_superuser => true}),
|
||||||
|
|
||||||
|
{200, UserRespd4} = request(get,
|
||||||
|
"/gateway/stomp/authentication/users/test"),
|
||||||
|
assert_confs(UserRespd4, User1#{is_superuser => true}),
|
||||||
|
|
||||||
|
{204, _} = request(delete, "/gateway/stomp/authentication/users/test"),
|
||||||
|
|
||||||
|
{200, #{data := []}} = request(get,
|
||||||
|
"/gateway/stomp/authentication/users"),
|
||||||
|
|
||||||
|
{204, _} = request(delete, "/gateway/stomp/authentication"),
|
||||||
|
{204, _} = request(get, "/gateway/stomp/authentication"),
|
||||||
|
{204, _} = request(delete, "/gateway/stomp").
|
||||||
|
|
||||||
t_listeners(_) ->
|
t_listeners(_) ->
|
||||||
GwConf = #{name => <<"stomp">>},
|
GwConf = #{name => <<"stomp">>},
|
||||||
{204, _} = request(post, "/gateway", GwConf),
|
{204, _} = request(post, "/gateway", GwConf),
|
||||||
|
@ -262,6 +306,65 @@ t_listeners_authn(_) ->
|
||||||
assert_confs(AuthConf2, ConfResp3),
|
assert_confs(AuthConf2, ConfResp3),
|
||||||
{204, _} = request(delete, "/gateway/stomp").
|
{204, _} = request(delete, "/gateway/stomp").
|
||||||
|
|
||||||
|
t_listeners_authn_data_mgmt(_) ->
|
||||||
|
GwConf = #{name => <<"stomp">>,
|
||||||
|
listeners => [
|
||||||
|
#{name => <<"def">>,
|
||||||
|
type => <<"tcp">>,
|
||||||
|
bind => <<"61613">>
|
||||||
|
}]},
|
||||||
|
{204, _} = request(post, "/gateway", GwConf),
|
||||||
|
{200, ConfResp} = request(get, "/gateway/stomp"),
|
||||||
|
assert_confs(GwConf, ConfResp),
|
||||||
|
|
||||||
|
AuthConf = #{mechanism => <<"password-based">>,
|
||||||
|
backend => <<"built-in-database">>,
|
||||||
|
user_id_type => <<"clientid">>
|
||||||
|
},
|
||||||
|
Path = "/gateway/stomp/listeners/stomp:tcp:def/authentication",
|
||||||
|
{204, _} = request(post, Path, AuthConf),
|
||||||
|
{200, ConfResp2} = request(get, Path),
|
||||||
|
assert_confs(AuthConf, ConfResp2),
|
||||||
|
|
||||||
|
User1 = #{ user_id => <<"test">>
|
||||||
|
, password => <<"123456">>
|
||||||
|
, is_superuser => false
|
||||||
|
},
|
||||||
|
{201, _} = request(post,
|
||||||
|
"/gateway/stomp/listeners/stomp:tcp:def/authentication/users",
|
||||||
|
User1),
|
||||||
|
|
||||||
|
{200,
|
||||||
|
#{data := [UserRespd1]} } = request(
|
||||||
|
get,
|
||||||
|
"/gateway/stomp/listeners/stomp:tcp:def/authentication/users"),
|
||||||
|
assert_confs(UserRespd1, User1),
|
||||||
|
|
||||||
|
{200, UserRespd2} = request(
|
||||||
|
get,
|
||||||
|
"/gateway/stomp/listeners/stomp:tcp:def/authentication/users/test"),
|
||||||
|
assert_confs(UserRespd2, User1),
|
||||||
|
|
||||||
|
{200, UserRespd3} = request(
|
||||||
|
put,
|
||||||
|
"/gateway/stomp/listeners/stomp:tcp:def/authentication/users/test",
|
||||||
|
#{password => <<"654321">>, is_superuser => true}),
|
||||||
|
assert_confs(UserRespd3, User1#{is_superuser => true}),
|
||||||
|
|
||||||
|
{200, UserRespd4} = request(
|
||||||
|
get,
|
||||||
|
"/gateway/stomp/listeners/stomp:tcp:def/authentication/users/test"),
|
||||||
|
assert_confs(UserRespd4, User1#{is_superuser => true}),
|
||||||
|
|
||||||
|
{204, _} = request(
|
||||||
|
delete,
|
||||||
|
"/gateway/stomp/listeners/stomp:tcp:def/authentication/users/test"),
|
||||||
|
|
||||||
|
{200, #{data := []}} = request(
|
||||||
|
get,
|
||||||
|
"/gateway/stomp/listeners/stomp:tcp:def/authentication/users"),
|
||||||
|
{204, _} = request(delete, "/gateway/stomp").
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
%% Asserts
|
%% Asserts
|
||||||
|
|
||||||
|
|
|
@ -164,7 +164,7 @@ t_random_test(_) ->
|
||||||
random_test_body() ->
|
random_test_body() ->
|
||||||
Data = generate_random_binary(),
|
Data = generate_random_binary(),
|
||||||
case catch parse(Data) of
|
case catch parse(Data) of
|
||||||
{ok, _Msg} -> ok;
|
Msg when is_record(Msg, mqtt_sn_message) -> ok;
|
||||||
{'EXIT', {Err, _Stack}}
|
{'EXIT', {Err, _Stack}}
|
||||||
when Err =:= unkown_message_type;
|
when Err =:= unkown_message_type;
|
||||||
Err =:= malformed_message_len;
|
Err =:= malformed_message_len;
|
||||||
|
|
Loading…
Reference in New Issue