408 lines
14 KiB
Erlang
408 lines
14 KiB
Erlang
%%--------------------------------------------------------------------
|
|
%% 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_authentication_api).
|
|
|
|
-export([ create_chain/2
|
|
, delete_chain/2
|
|
, lookup_chain/2
|
|
, list_chains/2
|
|
, add_service/2
|
|
, delete_service/2
|
|
, update_service/2
|
|
, lookup_service/2
|
|
, list_services/2
|
|
, move_service/2
|
|
, import_users/2
|
|
, add_user/2
|
|
, delete_user/2
|
|
, update_user/2
|
|
, lookup_user/2
|
|
, list_users/2
|
|
]).
|
|
|
|
-import(minirest, [return/1]).
|
|
|
|
-rest_api(#{name => create_chain,
|
|
method => 'POST',
|
|
path => "/authentication/chains",
|
|
func => create_chain,
|
|
descr => "Create a chain"
|
|
}).
|
|
|
|
-rest_api(#{name => delete_chain,
|
|
method => 'DELETE',
|
|
path => "/authentication/chains/:bin:id",
|
|
func => delete_chain,
|
|
descr => "Delete chain"
|
|
}).
|
|
|
|
-rest_api(#{name => lookup_chain,
|
|
method => 'GET',
|
|
path => "/authentication/chains/:bin:id",
|
|
func => lookup_chain,
|
|
descr => "Lookup chain"
|
|
}).
|
|
|
|
-rest_api(#{name => list_chains,
|
|
method => 'GET',
|
|
path => "/authentication/chains",
|
|
func => list_chains,
|
|
descr => "List all chains"
|
|
}).
|
|
|
|
-rest_api(#{name => add_service,
|
|
method => 'POST',
|
|
path => "/authentication/chains/:bin:id/services",
|
|
func => add_service,
|
|
descr => "Add service to chain"
|
|
}).
|
|
|
|
-rest_api(#{name => delete_service,
|
|
method => 'DELETE',
|
|
path => "/authentication/chains/:bin:id/services/:bin:service_name",
|
|
func => delete_service,
|
|
descr => "Delete service from chain"
|
|
}).
|
|
|
|
-rest_api(#{name => update_service,
|
|
method => 'PUT',
|
|
path => "/authentication/chains/:bin:id/services/:bin:service_name",
|
|
func => update_service,
|
|
descr => "Update service in chain"
|
|
}).
|
|
|
|
-rest_api(#{name => lookup_service,
|
|
method => 'GET',
|
|
path => "/authentication/chains/:bin:id/services/:bin:service_name",
|
|
func => lookup_service,
|
|
descr => "Lookup service in chain"
|
|
}).
|
|
|
|
-rest_api(#{name => list_services,
|
|
method => 'GET',
|
|
path => "/authentication/chains/:bin:id/services",
|
|
func => list_services,
|
|
descr => "List services in chain"
|
|
}).
|
|
|
|
-rest_api(#{name => move_service,
|
|
method => 'POST',
|
|
path => "/authentication/chains/:bin:id/services/:bin:service_name/position",
|
|
func => move_service,
|
|
descr => "Change the order of services"
|
|
}).
|
|
|
|
-rest_api(#{name => import_users,
|
|
method => 'POST',
|
|
path => "/authentication/chains/:bin:id/services/:bin:service_name/import-users",
|
|
func => import_users,
|
|
descr => "Import users"
|
|
}).
|
|
|
|
-rest_api(#{name => add_user,
|
|
method => 'POST',
|
|
path => "/authentication/chains/:bin:id/services/:bin:service_name/users",
|
|
func => add_user,
|
|
descr => "Add user"
|
|
}).
|
|
|
|
-rest_api(#{name => delete_user,
|
|
method => 'DELETE',
|
|
path => "/authentication/chains/:bin:id/services/:bin:service_name/users/:bin:user_id",
|
|
func => delete_user,
|
|
descr => "Delete user"
|
|
}).
|
|
|
|
-rest_api(#{name => update_user,
|
|
method => 'PUT',
|
|
path => "/authentication/chains/:bin:id/services/:bin:service_name/users/:bin:user_id",
|
|
func => update_user,
|
|
descr => "Update user"
|
|
}).
|
|
|
|
-rest_api(#{name => lookup_user,
|
|
method => 'GET',
|
|
path => "/authentication/chains/:bin:id/services/:bin:service_name/users/:bin:user_id",
|
|
func => lookup_user,
|
|
descr => "Lookup user"
|
|
}).
|
|
|
|
%% TODO: Support pagination
|
|
-rest_api(#{name => list_users,
|
|
method => 'GET',
|
|
path => "/authentication/chains/:bin:id/services/:bin:service_name/users",
|
|
func => list_users,
|
|
descr => "List all users"
|
|
}).
|
|
|
|
create_chain(Binding, Params) ->
|
|
do_create_chain(uri_decode(Binding), maps:from_list(Params)).
|
|
|
|
do_create_chain(_Binding, #{<<"id">> := ChainID}) ->
|
|
case emqx_authentication:create_chain(#{id => ChainID}) of
|
|
{ok, Chain} ->
|
|
return({ok, Chain});
|
|
{error, Reason} ->
|
|
return(serialize_error(Reason))
|
|
end;
|
|
do_create_chain(_Binding, _Params) ->
|
|
return(serialize_error({missing_parameter, id})).
|
|
|
|
delete_chain(Binding, Params) ->
|
|
do_delete_chain(uri_decode(Binding), maps:from_list(Params)).
|
|
|
|
do_delete_chain(#{id := ChainID}, _Params) ->
|
|
case emqx_authentication:delete_chain(ChainID) of
|
|
ok ->
|
|
return(ok);
|
|
{error, Reason} ->
|
|
return(serialize_error(Reason))
|
|
end.
|
|
|
|
lookup_chain(Binding, Params) ->
|
|
do_lookup_chain(uri_decode(Binding), maps:from_list(Params)).
|
|
|
|
do_lookup_chain(#{id := ChainID}, _Params) ->
|
|
case emqx_authentication:lookup_chain(ChainID) of
|
|
{ok, Chain} ->
|
|
return({ok, Chain});
|
|
{error, Reason} ->
|
|
return(serialize_error(Reason))
|
|
end.
|
|
|
|
list_chains(Binding, Params) ->
|
|
do_list_chains(uri_decode(Binding), maps:from_list(Params)).
|
|
|
|
do_list_chains(_Binding, _Params) ->
|
|
{ok, Chains} = emqx_authentication:list_chains(),
|
|
return({ok, Chains}).
|
|
|
|
add_service(Binding, Params) ->
|
|
do_add_service(uri_decode(Binding), maps:from_list(Params)).
|
|
|
|
do_add_service(#{id := ChainID}, #{<<"name">> := Name,
|
|
<<"type">> := Type,
|
|
<<"params">> := Params}) ->
|
|
case emqx_authentication:add_services(ChainID, [#{name => Name,
|
|
type => binary_to_existing_atom(Type, utf8),
|
|
params => maps:from_list(Params)}]) of
|
|
{ok, Services} ->
|
|
return({ok, Services});
|
|
{error, Reason} ->
|
|
return(serialize_error(Reason))
|
|
end;
|
|
%% TODO: Check missed field in params
|
|
do_add_service(_Binding, Params) ->
|
|
Missed = get_missed_params(Params, [<<"name">>, <<"type">>, <<"params">>]),
|
|
return(serialize_error({missing_parameter, Missed})).
|
|
|
|
delete_service(Binding, Params) ->
|
|
do_delete_service(uri_decode(Binding), maps:from_list(Params)).
|
|
|
|
do_delete_service(#{id := ChainID,
|
|
service_name := ServiceName}, _Params) ->
|
|
case emqx_authentication:delete_services(ChainID, [ServiceName]) of
|
|
ok ->
|
|
return(ok);
|
|
{error, Reason} ->
|
|
return(serialize_error(Reason))
|
|
end.
|
|
|
|
update_service(Binding, Params) ->
|
|
do_update_service(uri_decode(Binding), maps:from_list(Params)).
|
|
|
|
%% TOOD: PUT method supports creation and update
|
|
do_update_service(#{id := ChainID,
|
|
service_name := ServiceName}, Params) ->
|
|
case emqx_authentication:update_service(ChainID, ServiceName, Params) of
|
|
{ok, Service} ->
|
|
return({ok, Service});
|
|
{error, Reason} ->
|
|
return(serialize_error(Reason))
|
|
end.
|
|
|
|
lookup_service(Binding, Params) ->
|
|
do_lookup_service(uri_decode(Binding), maps:from_list(Params)).
|
|
|
|
do_lookup_service(#{id := ChainID,
|
|
service_name := ServiceName}, _Params) ->
|
|
case emqx_authentication:lookup_service(ChainID, ServiceName) of
|
|
{ok, Service} ->
|
|
return({ok, Service});
|
|
{error, Reason} ->
|
|
return(serialize_error(Reason))
|
|
end.
|
|
|
|
list_services(Binding, Params) ->
|
|
do_list_services(uri_decode(Binding), maps:from_list(Params)).
|
|
|
|
do_list_services(#{id := ChainID}, _Params) ->
|
|
case emqx_authentication:list_services(ChainID) of
|
|
{ok, Services} ->
|
|
return({ok, Services});
|
|
{error, Reason} ->
|
|
return(serialize_error(Reason))
|
|
end.
|
|
|
|
move_service(Binding, Params) ->
|
|
do_move_service(uri_decode(Binding), maps:from_list(Params)).
|
|
|
|
do_move_service(#{id := ChainID,
|
|
service_name := ServiceName}, #{<<"position">> := <<"the front">>}) ->
|
|
case emqx_authentication:move_service_to_the_front(ChainID, ServiceName) of
|
|
ok ->
|
|
return(ok);
|
|
{error, Reason} ->
|
|
return(serialize_error(Reason))
|
|
end;
|
|
do_move_service(#{id := ChainID,
|
|
service_name := ServiceName}, #{<<"position">> := <<"the end">>}) ->
|
|
case emqx_authentication:move_service_to_the_end(ChainID, ServiceName) of
|
|
ok ->
|
|
return(ok);
|
|
{error, Reason} ->
|
|
return(serialize_error(Reason))
|
|
end;
|
|
do_move_service(#{id := ChainID,
|
|
service_name := ServiceName}, #{<<"position">> := N}) when is_number(N) ->
|
|
case emqx_authentication:move_service_to_the_nth(ChainID, ServiceName, N) of
|
|
ok ->
|
|
return(ok);
|
|
{error, Reason} ->
|
|
return(serialize_error(Reason))
|
|
end;
|
|
do_move_service(_Binding, _Params) ->
|
|
return(serialize_error({missing_parameter, <<"position">>})).
|
|
|
|
import_users(Binding, Params) ->
|
|
do_import_users(uri_decode(Binding), maps:from_list(Params)).
|
|
|
|
do_import_users(#{id := ChainID, service_name := ServiceName},
|
|
#{<<"filename">> := Filename}) ->
|
|
case emqx_authentication:import_users(ChainID, ServiceName, Filename) of
|
|
ok ->
|
|
return(ok);
|
|
{error, Reason} ->
|
|
return(serialize_error(Reason))
|
|
end;
|
|
do_import_users(_Binding, Params) ->
|
|
Missed = get_missed_params(Params, [<<"filename">>, <<"file_format">>]),
|
|
return(serialize_error({missing_parameter, Missed})).
|
|
|
|
add_user(Binding, Params) ->
|
|
do_add_user(uri_decode(Binding), maps:from_list(Params)).
|
|
|
|
do_add_user(#{id := ChainID,
|
|
service_name := ServiceName}, UserInfo) ->
|
|
case emqx_authentication:add_user(ChainID, ServiceName, UserInfo) of
|
|
{ok, User} ->
|
|
return({ok, User});
|
|
{error, Reason} ->
|
|
return(serialize_error(Reason))
|
|
end.
|
|
|
|
delete_user(Binding, Params) ->
|
|
do_delete_user(uri_decode(Binding), maps:from_list(Params)).
|
|
|
|
do_delete_user(#{id := ChainID,
|
|
service_name := ServiceName,
|
|
user_id := UserID}, _Params) ->
|
|
case emqx_authentication:delete_user(ChainID, ServiceName, UserID) of
|
|
ok ->
|
|
return(ok);
|
|
{error, Reason} ->
|
|
return(serialize_error(Reason))
|
|
end.
|
|
|
|
update_user(Binding, Params) ->
|
|
do_update_user(uri_decode(Binding), maps:from_list(Params)).
|
|
|
|
do_update_user(#{id := ChainID,
|
|
service_name := ServiceName,
|
|
user_id := UserID}, NewUserInfo) ->
|
|
case emqx_authentication:update_user(ChainID, ServiceName, UserID, NewUserInfo) of
|
|
{ok, User} ->
|
|
return({ok, User});
|
|
{error, Reason} ->
|
|
return(serialize_error(Reason))
|
|
end.
|
|
|
|
lookup_user(Binding, Params) ->
|
|
do_lookup_user(uri_decode(Binding), maps:from_list(Params)).
|
|
|
|
do_lookup_user(#{id := ChainID,
|
|
service_name := ServiceName,
|
|
user_id := UserID}, _Params) ->
|
|
case emqx_authentication:lookup_user(ChainID, ServiceName, UserID) of
|
|
{ok, User} ->
|
|
return({ok, User});
|
|
{error, Reason} ->
|
|
return(serialize_error(Reason))
|
|
end.
|
|
|
|
list_users(Binding, Params) ->
|
|
do_list_users(uri_decode(Binding), maps:from_list(Params)).
|
|
|
|
do_list_users(#{id := ChainID,
|
|
service_name := ServiceName}, _Params) ->
|
|
case emqx_authentication:list_users(ChainID, ServiceName) of
|
|
{ok, Users} ->
|
|
return({ok, Users});
|
|
{error, Reason} ->
|
|
return(serialize_error(Reason))
|
|
end.
|
|
|
|
%%------------------------------------------------------------------------------
|
|
%% Internal functions
|
|
%%------------------------------------------------------------------------------
|
|
|
|
uri_decode(Params) ->
|
|
maps:fold(fun(K, V, Acc) ->
|
|
Acc#{K => emqx_http_lib:uri_decode(V)}
|
|
end, #{}, Params).
|
|
|
|
serialize_error({already_exists, {Type, ID}}) ->
|
|
{error, <<"ALREADY_EXISTS">>, list_to_binary(io_lib:format("~s '~s' already exists", [serialize_type(Type), ID]))};
|
|
serialize_error({not_found, {Type, ID}}) ->
|
|
{error, <<"NOT_FOUND">>, list_to_binary(io_lib:format("~s '~s' not found", [serialize_type(Type), ID]))};
|
|
serialize_error({duplicate, Name}) ->
|
|
{error, <<"INVALID_PARAMETER">>, list_to_binary(io_lib:format("Service name '~s' is duplicated", [Name]))};
|
|
serialize_error({missing_parameter, Names = [_ | Rest]}) ->
|
|
Format = ["~s," || _ <- Rest] ++ ["~s"],
|
|
NFormat = binary_to_list(iolist_to_binary(Format)),
|
|
{error, <<"MISSING_PARAMETER">>, list_to_binary(io_lib:format("The input parameters " ++ NFormat ++ " that are mandatory for processing this request are not supplied.", Names))};
|
|
serialize_error({missing_parameter, Name}) ->
|
|
{error, <<"MISSING_PARAMETER">>, list_to_binary(io_lib:format("The input parameter '~s' that is mandatory for processing this request is not supplied.", [Name]))};
|
|
serialize_error(_) ->
|
|
{error, <<"UNKNOWN_ERROR">>, <<"Unknown error">>}.
|
|
|
|
serialize_type(service) ->
|
|
"Service";
|
|
serialize_type(chain) ->
|
|
"Chain";
|
|
serialize_type(service_type) ->
|
|
"Service type".
|
|
|
|
get_missed_params(Actual, Expected) ->
|
|
Keys = lists:foldl(fun(Key, Acc) ->
|
|
case maps:is_key(Key, Actual) of
|
|
true -> Acc;
|
|
false -> [Key | Acc]
|
|
end
|
|
end, [], Expected),
|
|
lists:reverse(Keys).
|