172 lines
6.1 KiB
Erlang
172 lines
6.1 KiB
Erlang
%%--------------------------------------------------------------------
|
|
%% 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_app).
|
|
|
|
-behaviour(minirest_api).
|
|
|
|
-include_lib("typerefl/include/types.hrl").
|
|
|
|
-export([api_spec/0, fields/1, paths/0, schema/1, namespace/0]).
|
|
-export([api_key/2, api_key_by_name/2]).
|
|
-export([validate_name/1]).
|
|
|
|
namespace() -> "api_key".
|
|
|
|
api_spec() ->
|
|
emqx_dashboard_swagger:spec(?MODULE, #{check_schema => true, translate_body => true}).
|
|
|
|
paths() ->
|
|
["/api_key", "/api_key/:name"].
|
|
|
|
|
|
schema("/api_key") ->
|
|
#{
|
|
'operationId' => api_key,
|
|
get => #{
|
|
description => "Return api_key list",
|
|
responses => #{
|
|
200 => delete([api_secret], fields(app))
|
|
}
|
|
},
|
|
post => #{
|
|
description => "Create new api_key",
|
|
'requestBody' => delete([created_at, api_key, api_secret], fields(app)),
|
|
responses => #{
|
|
200 => hoconsc:ref(app)
|
|
}
|
|
}
|
|
};
|
|
schema("/api_key/:name") ->
|
|
#{
|
|
'operationId' => api_key_by_name,
|
|
get => #{
|
|
description => "Return the specific api_key",
|
|
parameters => [hoconsc:ref(name)],
|
|
responses => #{
|
|
200 => delete([api_secret], fields(app))
|
|
}
|
|
},
|
|
put => #{
|
|
description => "Update the specific api_key",
|
|
parameters => [hoconsc:ref(name)],
|
|
'requestBody' => delete([created_at, api_key, api_secret, name], fields(app)),
|
|
responses => #{
|
|
200 => delete([api_secret], fields(app))
|
|
}
|
|
},
|
|
delete => #{
|
|
description => "Delete the specific api_key",
|
|
parameters => [hoconsc:ref(name)],
|
|
responses => #{
|
|
204 => <<"Delete successfully">>
|
|
}
|
|
}
|
|
}.
|
|
|
|
fields(app) ->
|
|
[
|
|
{name, hoconsc:mk(binary(),
|
|
#{desc => "Unique and format by [a-zA-Z0-9-_]",
|
|
validator => fun ?MODULE:validate_name/1,
|
|
example => <<"EMQX-API-KEY-1">>})},
|
|
{api_key, hoconsc:mk(binary(),
|
|
#{desc => """TODO:uses HMAC-SHA256 for signing.""",
|
|
example => <<"a4697a5c75a769f6">>})},
|
|
{api_secret, hoconsc:mk(binary(),
|
|
#{desc => """An API secret is a simple encrypted string that identifies"""
|
|
"""an application without any principal."""
|
|
"""They are useful for accessing public data anonymously,"""
|
|
"""and are used to associate API requests.""",
|
|
example => <<"MzAyMjk3ODMwMDk0NjIzOTUxNjcwNzQ0NzQ3MTE2NDYyMDI">>})},
|
|
{expired_at, hoconsc:mk(emqx_schema:rfc3339_system_time(),
|
|
#{desc => "No longer valid datetime",
|
|
example => <<"2021-12-05T02:01:34.186Z">>,
|
|
nullable => true
|
|
})},
|
|
{created_at, hoconsc:mk(emqx_schema:rfc3339_system_time(),
|
|
#{desc => "ApiKey create datetime",
|
|
example => <<"2021-12-01T00:00:00.000Z">>
|
|
})},
|
|
{desc, hoconsc:mk(emqx_schema:unicode_binary(),
|
|
#{example => <<"Note">>, nullable => true})},
|
|
{enable, hoconsc:mk(boolean(), #{desc => "Enable/Disable", nullable => true})}
|
|
];
|
|
fields(name) ->
|
|
[{name, hoconsc:mk(binary(),
|
|
#{
|
|
desc => <<"[a-zA-Z0-9-_]">>,
|
|
example => <<"EMQX-API-KEY-1">>,
|
|
in => path,
|
|
validator => fun ?MODULE:validate_name/1
|
|
})}
|
|
].
|
|
|
|
-define(NAME_RE, "^[A-Za-z]+[A-Za-z0-9-_]*$").
|
|
|
|
validate_name(Name) ->
|
|
NameLen = byte_size(Name),
|
|
case NameLen > 0 andalso NameLen =< 256 of
|
|
true ->
|
|
case re:run(Name, ?NAME_RE) of
|
|
nomatch -> {error, "Name should be " ?NAME_RE};
|
|
_ -> ok
|
|
end;
|
|
false -> {error, "Name Length must =< 256"}
|
|
end.
|
|
|
|
delete(Keys, Fields) ->
|
|
lists:foldl(fun(Key, Acc) -> lists:keydelete(Key, 1, Acc) end, Fields, Keys).
|
|
|
|
api_key(get, _) ->
|
|
{200, [format(App) || App <- emqx_mgmt_auth:list()]};
|
|
api_key(post, #{body := App}) ->
|
|
#{
|
|
<<"name">> := Name,
|
|
<<"desc">> := Desc0,
|
|
<<"expired_at">> := ExpiredAt,
|
|
<<"enable">> := Enable
|
|
} = App,
|
|
Desc = unicode:characters_to_binary(Desc0, unicode),
|
|
case emqx_mgmt_auth:create(Name, Enable, ExpiredAt, Desc) of
|
|
{ok, NewApp} -> {200, format(NewApp)};
|
|
{error, Reason} -> {400, Reason}
|
|
end.
|
|
|
|
api_key_by_name(get, #{bindings := #{name := Name}}) ->
|
|
case emqx_mgmt_auth:read(Name) of
|
|
{ok, App} -> {200, format(App)};
|
|
{error, not_found} -> {404, <<"NOT_FOUND">>}
|
|
end;
|
|
api_key_by_name(delete, #{bindings := #{name := Name}}) ->
|
|
case emqx_mgmt_auth:delete(Name) of
|
|
{ok, _} -> {204};
|
|
{error, not_found} -> {404, <<"NOT_FOUND">>}
|
|
end;
|
|
api_key_by_name(put, #{bindings := #{name := Name}, body := Body}) ->
|
|
Enable = maps:get(<<"enable">>, Body, undefined),
|
|
ExpiredAt = maps:get(<<"expired_at">>, Body, undefined),
|
|
Desc = maps:get(<<"desc">>, Body, undefined),
|
|
case emqx_mgmt_auth:update(Name, Enable, ExpiredAt, Desc) of
|
|
{ok, App} -> {200, format(App)};
|
|
{error, not_found} -> {404, <<"NOT_FOUND">>}
|
|
end.
|
|
|
|
format(App = #{expired_at := ExpiredAt, created_at := CreateAt}) ->
|
|
App#{
|
|
expired_at => list_to_binary(calendar:system_time_to_rfc3339(ExpiredAt)),
|
|
created_at => list_to_binary(calendar:system_time_to_rfc3339(CreateAt))
|
|
}.
|