diff --git a/apps/emqx_authentication/data/user-credentials.csv b/apps/emqx_authentication/data/user-credentials.csv index 7ee4fe8f1..98bd92d52 100644 --- a/apps/emqx_authentication/data/user-credentials.csv +++ b/apps/emqx_authentication/data/user-credentials.csv @@ -1,2 +1,2 @@ -myuser3,mypassword3 -myuser4,mypassword4 \ No newline at end of file +myuser3,8d41233e39c95b5da13361e354e1c9e639f07b27d397463a8f91b71ee07ccfb2 +myuser4,5809df0154f3cb4ac5c3a5572eaca0c5f7f9d858e887fc675b2becab9feb19d1 \ No newline at end of file diff --git a/apps/emqx_authentication/data/user-credentials.json b/apps/emqx_authentication/data/user-credentials.json index 6c4689433..a153be616 100644 --- a/apps/emqx_authentication/data/user-credentials.json +++ b/apps/emqx_authentication/data/user-credentials.json @@ -1,4 +1,4 @@ { - "myuser1": "mypassword1", - "myuser2": "mypassword2" + "myuser1": "09343625c6c123d3434932fe1ce08bae5ac00a8f95bd746e10491b0bafdd1817", + "myuser2": "8767a7d316ad68cb607c7c805b859ffa78277dda13b7a3e2e8b53cad3cabbc6e" } \ No newline at end of file diff --git a/apps/emqx_authentication/src/emqx_authentication.erl b/apps/emqx_authentication/src/emqx_authentication.erl index 84d730706..1ee5f46bc 100644 --- a/apps/emqx_authentication/src/emqx_authentication.erl +++ b/apps/emqx_authentication/src/emqx_authentication.erl @@ -40,11 +40,12 @@ , move_service_to_the_nth/3 ]). --export([ import_user_credentials/4 - , add_user_credential/3 - , delete_user_credential/3 - , update_user_credential/3 - , lookup_user_credential/3 +-export([ import_users/3 + , add_user/3 + , delete_user/3 + , update_user/4 + , lookup_user/3 + , list_users/2 ]). -export([mnesia/1]). @@ -101,7 +102,7 @@ disable() -> authenticate(#{chain_id := ChainID} = ClientInfo) -> case mnesia:dirty_read(?CHAIN_TAB, ChainID) of [#chain{services = []}] -> - {error, todo}; + {error, no_services}; [#chain{services = Services}] -> do_authenticate(Services, ClientInfo); [] -> @@ -109,7 +110,7 @@ authenticate(#{chain_id := ChainID} = ClientInfo) -> end. do_authenticate([], _) -> - {error, user_credential_not_found}; + {error, user_not_found}; do_authenticate([{_, #service{provider = Provider, state = State}} | More], ClientInfo) -> case Provider:authenticate(ClientInfo, State) of ignore -> do_authenticate(More, ClientInfo); @@ -136,55 +137,44 @@ register_service_types([{_App, Mod, #{name := Name, params_spec = ParamsSpec}, register_service_types(Types, [ServiceType | Acc]). -create_chain(Params = #{chain_id := ChainID}) -> - ServiceParams = maps:get(services, Params, []), - case validate_service_params(ServiceParams) of - {ok, NServiceParams} -> - trans( - fun() -> - case mnesia:read(?CHAIN_TAB, ChainID, write) of - [] -> - case create_services(ChainID, NServiceParams) of - {ok, Services} -> - Chain = #chain{id = ChainID, - services = Services, - created_at = erlang:system_time(millisecond)}, - mnesia:write(?CHAIN_TAB, Chain, write), - {ok, ChainID}; - {error, Reason} -> - {error, Reason} - end; - [_ | _] -> - {error, {already_exists, {chain, ChainID}}} - end - end); - {error, Reason} -> - {error, Reason} - end. - -delete_chain(ChainID) -> +create_chain(#{id := ID}) -> trans( fun() -> - case mnesia:read(?CHAIN_TAB, ChainID, write) of + case mnesia:read(?CHAIN_TAB, ID, write) of [] -> - {error, {not_found, {chain, ChainID}}}; - [#chain{services = Services}] -> - ok = delete_services_(Services), - mnesia:delete(?CHAIN_TAB, ChainID, write) + Chain = #chain{id = ID, + services = [], + created_at = erlang:system_time(millisecond)}, + mnesia:write(?CHAIN_TAB, Chain, write), + {ok, serialize_chain(Chain)}; + [_ | _] -> + {error, {already_exists, {chain, ID}}} end end). -lookup_chain(ChainID) -> - case mnesia:dirty_read(?CHAIN_TAB, ChainID) of +delete_chain(ID) -> + trans( + fun() -> + case mnesia:read(?CHAIN_TAB, ID, write) of + [] -> + {error, {not_found, {chain, ID}}}; + [#chain{services = Services}] -> + ok = delete_services_(Services), + mnesia:delete(?CHAIN_TAB, ID, write) + end + end). + +lookup_chain(ID) -> + case mnesia:dirty_read(?CHAIN_TAB, ID) of [] -> - {error, {not_found, {chain, ChainID}}}; + {error, {not_found, {chain, ID}}}; [Chain] -> {ok, serialize_chain(Chain)} end. list_chains() -> Chains = ets:tab2list(?CHAIN_TAB), - [serialize_chain(Chain) || Chain <- Chains]. + {ok, [serialize_chain(Chain) || Chain <- Chains]}. add_services(ChainID, ServiceParams) -> case validate_service_params(ServiceParams) of @@ -195,8 +185,10 @@ add_services(ChainID, ServiceParams) -> ok -> case create_services(ChainID, NServiceParams) of {ok, NServices} -> + io:format("~p~n", [NServices]), NChain = Chain#chain{services = Services ++ NServices}, - mnesia:write(?CHAIN_TAB, NChain, write); + ok = mnesia:write(?CHAIN_TAB, NChain, write), + {ok, serialize_services(NServices)}; {error, Reason} -> {error, Reason} end; @@ -243,8 +235,9 @@ update_service(ChainID, ServiceName, NewParams) -> {ok, NState} -> NService = Service#service{params = Params, state = NState}, - NServices = lists:keyreplace(ServiceName, 1, Services, [{ServiceName, NService}]), - mnesia:write(?CHAIN_TAB, Chain#chain{services = NServices}, write); + NServices = lists:keyreplace(ServiceName, 1, Services, {ServiceName, NService}), + ok = mnesia:write(?CHAIN_TAB, Chain#chain{services = NServices}, write), + {ok, serialize_service({ServiceName, NService})}; {error, Reason} -> {error, Reason} end @@ -270,7 +263,7 @@ list_services(ChainID) -> [] -> {error, {not_found, {chain, ChainID}}}; [#chain{services = Services}] -> - {ok, [serialize_service(Service) || Service <- Services]} + {ok, serialize_services(Services)} end. move_service_to_the_front(ChainID, ServiceName) -> @@ -309,20 +302,23 @@ move_service_to_the_nth(ChainID, ServiceName, N) -> end, update_chain(ChainID, UpdateFun). -import_user_credentials(ChainID, ServiceName, Filename, FileFormat) -> - call_service(ChainID, ServiceName, import_user_credentials, [Filename, FileFormat]). +import_users(ChainID, ServiceName, Filename) -> + call_service(ChainID, ServiceName, import_users, [Filename]). -add_user_credential(ChainID, ServiceName, Credential) -> - call_service(ChainID, ServiceName, add_user_credential, [Credential]). +add_user(ChainID, ServiceName, UserInfo) -> + call_service(ChainID, ServiceName, add_user, [UserInfo]). -delete_user_credential(ChainID, ServiceName, UserIdentity) -> - call_service(ChainID, ServiceName, delete_user_credential, [UserIdentity]). +delete_user(ChainID, ServiceName, UserID) -> + call_service(ChainID, ServiceName, delete_user, [UserID]). -update_user_credential(ChainID, ServiceName, Credential) -> - call_service(ChainID, ServiceName, update_user_credential, [Credential]). +update_user(ChainID, ServiceName, UserID, NewUserInfo) -> + call_service(ChainID, ServiceName, update_user, [UserID, NewUserInfo]). -lookup_user_credential(ChainID, ServiceName, UserIdentity) -> - call_service(ChainID, ServiceName, lookup_user_credential, [UserIdentity]). +lookup_user(ChainID, ServiceName, UserID) -> + call_service(ChainID, ServiceName, lookup_user, [UserID]). + +list_users(ChainID, ServiceName) -> + call_service(ChainID, ServiceName, list_users, []). %%------------------------------------------------------------------------------ %% Internal functions @@ -506,9 +502,12 @@ serialize_chain(#chain{id = ID, services = Services, created_at = CreatedAt}) -> #{id => ID, - services => [serialize_service(Service) || Service <- Services], + services => serialize_services(Services), created_at => CreatedAt}. +serialize_services(Services) -> + [serialize_service(Service) || Service <- Services]. + serialize_service({_, #service{name = Name, type = Type, params = Params}}) -> diff --git a/apps/emqx_authentication/src/emqx_authentication_api.erl b/apps/emqx_authentication/src/emqx_authentication_api.erl index a3ed80250..443091664 100644 --- a/apps/emqx_authentication/src/emqx_authentication_api.erl +++ b/apps/emqx_authentication/src/emqx_authentication_api.erl @@ -20,25 +20,22 @@ , delete_chain/2 , lookup_chain/2 , list_chains/2 - , add_services/2 - , delete_services/2 + , add_service/2 + , delete_service/2 , update_service/2 , lookup_service/2 , list_services/2 , move_service/2 - , import_user_credentials/2 - , add_user_creadential/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 => list_chains, - method => 'GET', - path => "/authentication/chains", - func => list_chains, - descr => "List all chains" - }). - -rest_api(#{name => create_chain, method => 'POST', path => "/authentication/chains", @@ -46,187 +43,352 @@ descr => "Create a chain" }). -create_chain(_Binding, Params = #{chain_id := ChainID}) -> - case emqx_authentication:create_chain(Params) of - {ok, ChainID} -> - return({ok, ChainID}); - {error, Reason} -> - return({error, serialize_error(Reason)}) - end; -create_chain(_Binding, _Params) -> - return({error, serialize_error({missing_parameter, chain_id})}). +-rest_api(#{name => delete_chain, + method => 'DELETE', + path => "/authentication/chains/:bin:chain_id", + func => delete_chain, + descr => "Delete chain" + }). -delete_chain(_Binding, #{chain_id := ChainID}) -> +-rest_api(#{name => lookup_chain, + method => 'GET', + path => "/authentication/chains/:bin:chain_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:chain_id/services", + func => add_service, + descr => "Add service to chain" + }). + +-rest_api(#{name => delete_service, + method => 'DELETE', + path => "/authentication/chains/:bin:chain_id/services/:bin:service_name", + func => delete_service, + descr => "Delete service from chain" + }). + +-rest_api(#{name => update_service, + method => 'PUT', + path => "/authentication/chains/:bin:chain_id/services/:bin:service_name", + func => update_service, + descr => "Update service in chain" + }). + +-rest_api(#{name => lookup_service, + method => 'GET', + path => "/authentication/chains/:bin:chain_id/services/:bin:service_name", + func => lookup_service, + descr => "Lookup service in chain" + }). + +-rest_api(#{name => list_services, + method => 'GET', + path => "/authentication/chains/:bin:chain_id/services", + func => list_services, + descr => "List services in chain" + }). + +-rest_api(#{name => move_service, + method => 'POST', + path => "/authentication/chains/:bin:chain_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:chain_id/services/:bin:service_name/import-users", + func => import_users, + descr => "Import users" + }). + +-rest_api(#{name => add_user, + method => 'POST', + path => "/authentication/chains/:bin:chain_id/services/:bin:service_name/users", + func => add_user, + descr => "Add user" + }). + +-rest_api(#{name => delete_user, + method => 'DELETE', + path => "/authentication/chains/:bin:chain_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:chain_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:chain_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:chain_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, #{<<"chain_id">> := ChainID}) -> + case emqx_authentication:create_chain(ChainID) of + {ok, Chain} -> + return({ok, Chain}); + {error, Reason} -> + return(serialize_error(Reason)) + end; +do_create_chain(_Binding, _Params) -> + return(serialize_error({missing_parameter, chain_id})). + +delete_chain(Binding, Params) -> + do_delete_chain(uri_decode(Binding), maps:from_list(Params)). + +do_delete_chain(#{chain_id := ChainID}, _Params) -> case emqx_authentication:delete_chain(ChainID) of ok -> return(ok); {error, Reason} -> - return({error, serialize_error(Reason)}) + return(serialize_error(Reason)) end. -lookup_chain(_Binding, #{chain_id := ChainID}) -> +lookup_chain(Binding, Params) -> + do_lookup_chain(uri_decode(Binding), maps:from_list(Params)). + +do_lookup_chain(#{chain_id := ChainID}, _Params) -> case emqx_authentication:lookup_chain(ChainID) of {ok, Chain} -> return({ok, Chain}); {error, Reason} -> - return({error, serialize_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(#{chain_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; -lookup_chain(_Binding, _Params) -> - return({error, serialize_error({missing_parameter, chain_id})}). +%% 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})). -list_chains(_Binding, _Params) -> - emqx_authentication:list_chains(). +delete_service(Binding, Params) -> + do_delete_service(uri_decode(Binding), maps:from_list(Params)). -add_services(_Binding, Params = #{chain_id := ChainID}) -> - case maps:get(services, Params, []) of - [] -> return(ok); - Services -> - case emqx_authentication:add_services(ChainID, Services) of - ok -> - return(ok); - {error, Reason} -> - return({error, serialize_error(Reason)}) - end - end; -add_services(_Binding, _Params) -> - return({error, serialize_error({missing_parameter, chain_id})}). - -delete_services(_Binding, #{chain_id := ChainID, - service_names := ServiceNames}) -> - case emqx_authentication:delete_services(ChainID, ServiceNames) of +do_delete_service(#{chain_id := ChainID, + service_name := ServiceName}, _Params) -> + case emqx_authentication:delete_services(ChainID, [ServiceName]) of ok -> return(ok); {error, Reason} -> - return({error, serialize_error(Reason)}) - end; -delete_services(_Binding, #{chain_id := _}) -> - return({error, serialize_error({missing_parameter, service_names})}); -delete_services(_Binding, #{service_names := _}) -> - return({error, serialize_error({missing_parameter, chain_id})}). + return(serialize_error(Reason)) + end. -%% TODO: better input parameters -update_service(_Binding, #{chain_id := ChainID, - service_name := ServiceName, - service_params := Params}) -> +update_service(Binding, Params) -> + do_update_service(uri_decode(Binding), maps:from_list(Params)). + +%% TOOD: PUT 方法支持创建和更新 +do_update_service(#{chain_id := ChainID, + service_name := ServiceName}, Params) -> case emqx_authentication:update_service(ChainID, ServiceName, Params) of - ok -> - return(ok); + {ok, Service} -> + return({ok, Service}); {error, Reason} -> - return({error, serialize_error(Reason)}) - end; -update_service(_Binding, #{chain_id := _}) -> - return({error, serialize_error({missing_parameter, service_name})}); -update_service(_Binding, #{service_name := _}) -> - return({error, serialize_error({missing_parameter, chain_id})}). + return(serialize_error(Reason)) + end. -lookup_service(_Binding, #{chain_id := ChainID, - service_name := ServiceName}) -> +lookup_service(Binding, Params) -> + do_lookup_service(uri_decode(Binding), maps:from_list(Params)). + +do_lookup_service(#{chain_id := ChainID, + service_name := ServiceName}, _Params) -> case emqx_authentication:lookup_service(ChainID, ServiceName) of {ok, Service} -> return({ok, Service}); {error, Reason} -> - return({error, serialize_error(Reason)}) - end; -lookup_service(_Binding, #{chain_id := _}) -> - return({error, serialize_error({missing_parameter, service_name})}); -lookup_service(_Binding, #{service_name := _}) -> - return({error, serialize_error({missing_parameter, chain_id})}). + return(serialize_error(Reason)) + end. -list_services(_Binding, #{chain_id := ChainID}) -> +list_services(Binding, Params) -> + do_list_services(uri_decode(Binding), maps:from_list(Params)). + +do_list_services(#{chain_id := ChainID}, _Params) -> case emqx_authentication:list_services(ChainID) of - {ok, Service} -> - return({ok, Service}); + {ok, Services} -> + return({ok, Services}); {error, Reason} -> - return({error, serialize_error(Reason)}) - end; -list_services(_Binding, _Params) -> - return({error, serialize_error({missing_parameter, chain_id})}). + return(serialize_error(Reason)) + end. -move_service(_Binding, #{chain_id := ChainID, - service_name := ServiceName, - to := <<"the front">>}) -> - case emqx_authenticaiton:move_service_to_the_front(ChainID, ServiceName) of - ok -> - return(ok); - {error, Reason} -> - return({error, serialize_error(Reason)}) - end; -move_service(_Binding, #{chain_id := ChainID, - service_name := ServiceName, - to := <<"the end">>}) -> - case emqx_authenticaiton:move_service_to_the_end(ChainID, ServiceName) of - ok -> - return(ok); - {error, Reason} -> - return({error, serialize_error(Reason)}) - end; -move_service(_Binding, #{chain_id := ChainID, - service_name := ServiceName, - to := N}) when is_number(N) -> - case emqx_authenticaiton:move_service_to_the_nth(ChainID, ServiceName, N) of - ok -> - return(ok); - {error, Reason} -> - return({error, serialize_error(Reason)}) - end; -move_service(_Binding, Params) -> - Missed = get_missed_params(Params, [chain_id, service_name, to]), - return({error, serialize_error({missing_parameter, Missed})}). +move_service(Binding, Params) -> + do_move_service(uri_decode(Binding), maps:from_list(Params)). -import_user_credentials(_Binding, #{chain_id := ChainID, - service_name := ServiceName, - filename := Filename, - file_format := FileFormat}) -> - case emqx_authentication:import_user_credentials(ChainID, ServiceName, Filename, FileFormat) of +do_move_service(#{chain_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({error, serialize_error(Reason)}) + return(serialize_error(Reason)) end; -import_user_credentials(_Binding, Params) -> - Missed = get_missed_params(Params, [chain_id, service_name, filename, file_format]), - return({error, serialize_error({missing_parameter, Missed})}). +do_move_service(#{chain_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(#{chain_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">>})). -add_user_creadential(_Binding, #{chain_id := ChainID, - service_name := ServiceName, - credential := Credential}) -> - case emqx_authentication:add_user_creadentials(ChainID, ServiceName, Credential) of +import_users(Binding, Params) -> + do_import_users(uri_decode(Binding), maps:from_list(Params)). + +do_import_users(#{chain_id := ChainID, service_name := ServiceName}, + #{<<"filename">> := Filename}) -> + case emqx_authentication:import_users(ChainID, ServiceName, Filename) of ok -> return(ok); {error, Reason} -> - return({error, serialize_error(Reason)}) + return(serialize_error(Reason)) end; -add_user_creadential(_Binding, Params) -> - Missed = get_missed_params(Params, [chain_id, service_name, credential]), - return({error, serialize_error({missing_parameter, Missed})}). +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(#{chain_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(#{chain_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(#{chain_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(#{chain_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(#{chain_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 %%------------------------------------------------------------------------------ -serialize_error(Reason) when not is_map(Reason) -> - Error = serialize_error_(Reason), - emqx_json:encode(Error). +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}}) -> - #{code => "ALREADY_EXISTS", - message => io_lib:format("~p ~p already exists", [serialize_type(Type), ID])}; -serialize_error_({not_found, {Type, ID}}) -> - #{code => "NOT_FOUND", - message => io_lib:format("~p ~p not found", [serialize_type(Type), ID])}; -serialize_error_({duplicate, Name}) -> - #{code => "INVALID_PARAMETER", - message => io_lib:format("Service name ~p is duplicated", [Name])}; -serialize_error_({missing_parameter, Names = [_ | Rest]}) -> +serialize_error({already_exists, {Type, ID}}) -> + {error, <<"ALREADY_EXISTS">>, list_to_binary(io_lib:format("~p ~p already exists", [serialize_type(Type), ID]))}; +serialize_error({not_found, {Type, ID}}) -> + {error, <<"NOT_FOUND">>, list_to_binary(io_lib:format("~p ~p not found", [serialize_type(Type), ID]))}; +serialize_error({duplicate, Name}) -> + {error, <<"INVALID_PARAMETER">>, list_to_binary(io_lib:format("Service name ~p is duplicated", [Name]))}; +serialize_error({missing_parameter, Names = [_ | Rest]}) -> Format = ["~p," || _ <- Rest] ++ ["~p"], NFormat = binary_to_list(iolist_to_binary(Format)), - #{code => "MISSING_PARAMETER", - message => io_lib:format("The input parameters " ++ NFormat ++ " that are mandatory for processing this request are not supplied.", Names)}; -serialize_error_({missing_parameter, Name}) -> - #{code => "MISSING_PARAMETER", - message => io_lib:format("The input parameter ~p that is mandatory for processing this request is not supplied.", [Name])}; -serialize_error_(_) -> - #{code => "UNKNOWN_ERROR"}. + {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 ~p that is mandatory for processing this request is not supplied.", [Name]))}; +serialize_error(_) -> + {error, <<"UNKNOWN_ERROR">>, <<"Unknown error">>}. serialize_type(service) -> "Service"; diff --git a/apps/emqx_authentication/src/emqx_authentication_app.erl b/apps/emqx_authentication/src/emqx_authentication_app.erl index 473375455..bfdbefb36 100644 --- a/apps/emqx_authentication/src/emqx_authentication_app.erl +++ b/apps/emqx_authentication/src/emqx_authentication_app.erl @@ -18,6 +18,8 @@ -behaviour(application). +-emqx_plugin(?MODULE). + %% Application callbacks -export([ start/2 , stop/1 diff --git a/apps/emqx_authentication/src/emqx_authentication_mnesia.erl b/apps/emqx_authentication/src/emqx_authentication_mnesia.erl index 63b37f05d..9cc077452 100644 --- a/apps/emqx_authentication/src/emqx_authentication_mnesia.erl +++ b/apps/emqx_authentication/src/emqx_authentication_mnesia.erl @@ -24,17 +24,18 @@ , destroy/1 ]). --export([ import_user_credentials/3 - , add_user_credential/2 - , delete_user_credential/2 - , update_user_credential/2 - , lookup_user_credential/2 +-export([ import_users/2 + , add_user/2 + , delete_user/2 + , update_user/3 + , lookup_user/2 + , list_users/1 ]). -service_type(#{ name => mnesia, params_spec => #{ - user_identity_type => #{ + user_id_type => #{ order => 1, type => string, required => true, @@ -51,13 +52,13 @@ } }). --record(user_credential, - { user_identity :: {user_group(), user_identity()} +-record(user_info, + { user_id :: {user_group(), user_id()} , password_hash :: binary() }). -type(user_group() :: {chain_id(), service_name()}). --type(user_identity() :: binary()). +-type(user_id() :: binary()). -export([mnesia/1]). @@ -66,6 +67,8 @@ -define(TAB, mnesia_basic_auth). +%% TODO: Support salt + %%------------------------------------------------------------------------------ %% Mnesia bootstrap %%------------------------------------------------------------------------------ @@ -75,17 +78,17 @@ mnesia(boot) -> ok = ekka_mnesia:create_table(?TAB, [ {disc_copies, [node()]}, - {record_name, user_credential}, - {attributes, record_info(fields, user_credential)}, + {record_name, user_info}, + {attributes, record_info(fields, user_info)}, {storage_properties, [{ets, [{read_concurrency, true}]}]}]); mnesia(copy) -> ok = ekka_mnesia:copy_table(?TAB, disc_copies). -create(ChainID, ServiceName, #{<<"user_identity_type">> := Type, +create(ChainID, ServiceName, #{<<"user_id_type">> := Type, <<"password_hash_algorithm">> := Algorithm}) -> State = #{user_group => {ChainID, ServiceName}, - user_identity_type => binary_to_atom(Type, utf8), + user_id_type => binary_to_atom(Type, utf8), password_hash_algorithm => binary_to_atom(Algorithm, utf8)}, {ok, State}. @@ -94,13 +97,13 @@ update(ChainID, ServiceName, Params, _State) -> authenticate(ClientInfo = #{password := Password}, #{user_group := UserGroup, - user_identity_type := Type, + user_id_type := Type, password_hash_algorithm := Algorithm}) -> - UserIdentity = get_user_identity(ClientInfo, Type), - case mnesia:dirty_read(?TAB, {UserGroup, UserIdentity}) of + UserID = get_user_identity(ClientInfo, Type), + case mnesia:dirty_read(?TAB, {UserGroup, UserID}) of [] -> ignore; - [#user_credential{password_hash = Hash}] -> + [#user_info{password_hash = Hash}] -> case Hash =:= emqx_passwd:hash(Algorithm, Password) of true -> ok; @@ -112,117 +115,133 @@ authenticate(ClientInfo = #{password := Password}, destroy(#{user_group := UserGroup}) -> trans( fun() -> - MatchSpec = [{#user_credential{user_identity = {UserGroup, '_'}, _ = '_'}, [], ['$_']}], - lists:foreach(fun delete_user_credential/1, mnesia:select(?TAB, MatchSpec, write)) + MatchSpec = [{#user_info{user_id = {UserGroup, '_'}, _ = '_'}, [], ['$_']}], + lists:foreach(fun delete_user2/1, mnesia:select(?TAB, MatchSpec, write)) end). +import_users(Filename0, State) -> + Filename = to_binary(Filename0), + case filename:extension(Filename) of + <<".json">> -> + import_users_from_json(Filename, State); + <<".csv">> -> + import_users_from_csv(Filename, State); + <<>> -> + {error, unknown_file_format}; + Extension -> + {error, {unsupported_file_format, Extension}} + end. + +add_user(#{<<"user_id">> := UserID, + <<"password">> := Password}, + #{user_group := UserGroup, + password_hash_algorithm := Algorithm}) -> + trans( + fun() -> + case mnesia:read(?TAB, {UserGroup, UserID}, write) of + [] -> + add(UserGroup, UserID, Password, Algorithm), + {ok, #{user_id => UserID}}; + [_] -> + {error, already_exist} + end + end). + +delete_user(UserID, #{user_group := UserGroup}) -> + trans( + fun() -> + case mnesia:read(?TAB, {UserGroup, UserID}, write) of + [] -> + {error, not_found}; + [_] -> + mnesia:delete(?TAB, {UserGroup, UserID}, write) + end + end). + +update_user(UserID, #{<<"password">> := Password}, + #{user_group := UserGroup, + password_hash_algorithm := Algorithm}) -> + trans( + fun() -> + case mnesia:read(?TAB, {UserGroup, UserID}, write) of + [] -> + {error, not_found}; + [_] -> + add(UserGroup, UserID, Password, Algorithm), + {ok, #{user_id => UserID}} + end + end). + +lookup_user(UserID, #{user_group := UserGroup}) -> + case mnesia:dirty_read(?TAB, {UserGroup, UserID}) of + [#user_info{user_id = {_, UserID}}] -> + {ok, #{user_id => UserID}}; + [] -> + {error, not_found} + end. + +list_users(#{user_group := UserGroup}) -> + Users = [#{user_id => UserID} || #user_info{user_id = {UserGroup0, UserID}} <- ets:tab2list(?TAB), UserGroup0 =:= UserGroup], + {ok, Users}. + +%%------------------------------------------------------------------------------ +%% Internal functions +%%------------------------------------------------------------------------------ + %% Example: %% { -%% "myuser1":"mypassword1", -%% "myuser2":"mypassword2" +%% "myuser1":"password_hash1", +%% "myuser2":"password_hash2" %% } -import_user_credentials(Filename, json, - #{user_group := UserGroup, - password_hash_algorithm := Algorithm}) -> +import_users_from_json(Filename, #{user_group := UserGroup}) -> case file:read_file(Filename) of {ok, Bin} -> case emqx_json:safe_decode(Bin) of {ok, List} -> - import(UserGroup, List, Algorithm); + import(UserGroup, List); {error, Reason} -> {error, Reason} end; {error, Reason} -> {error, Reason} - end; + end. + %% Example: -%% myuser1,mypassword1 -%% myuser2,mypassword2 -import_user_credentials(Filename, csv, - #{user_group := UserGroup, - password_hash_algorithm := Algorithm}) -> +%% myuser1,password_hash1 +%% myuser2,password_hash2 +import_users_from_csv(Filename, #{user_group := UserGroup}) -> case file:open(Filename, [read, binary]) of {ok, File} -> - Result = import(UserGroup, File, Algorithm), + Result = import(UserGroup, File), file:close(File), Result; {error, Reason} -> {error, Reason} end. -add_user_credential(#{user_identity := UserIdentity, password := Password}, - #{user_group := UserGroup, - password_hash_algorithm := Algorithm}) -> - trans( - fun() -> - case mnesia:read(?TAB, {UserGroup, UserIdentity}, write) of - [] -> - add(UserGroup, UserIdentity, Password, Algorithm); - [_] -> - {error, already_exist} - end - end). +import(UserGroup, ListOrFile) -> + trans(fun do_import/2, [UserGroup, ListOrFile]). -delete_user_credential(UserIdentity, #{user_group := UserGroup}) -> - trans( - fun() -> - case mnesia:read(?TAB, {UserGroup, UserIdentity}, write) of - [] -> - {error, not_found}; - [_] -> - mnesia:delete(?TAB, {UserGroup, UserIdentity}, write) - end - end). - -update_user_credential(#{user_identity := UserIdentity, password := Password}, - #{user_group := UserGroup, - password_hash_algorithm := Algorithm}) -> - trans( - fun() -> - case mnesia:read(?TAB, {UserGroup, UserIdentity}, write) of - [] -> - {error, not_found}; - [_] -> - add(UserGroup, UserIdentity, Password, Algorithm) - end - end). - -lookup_user_credential(UserIdentity, #{user_group := UserGroup}) -> - case mnesia:dirty_read(?TAB, {UserGroup, UserIdentity}) of - [#user_credential{user_identity = {_, UserIdentity}, - password_hash = PassHash}] -> - {ok, #{user_identity => UserIdentity, - password_hash => PassHash}}; - [] -> {error, not_found} - end. - -%%------------------------------------------------------------------------------ -%% Internal functions -%%------------------------------------------------------------------------------ - -import(UserGroup, ListOrFile, Algorithm) -> - trans(fun do_import/3, [UserGroup, ListOrFile, Algorithm]). - -do_import(_UserGroup, [], _Algorithm) -> +do_import(_UserGroup, []) -> ok; -do_import(UserGroup, [{UserIdentity, Password} | More], Algorithm) - when is_binary(UserIdentity) andalso is_binary(Password) -> - add(UserGroup, UserIdentity, Password, Algorithm), - do_import(UserGroup, More, Algorithm); -do_import(_UserGroup, [_ | _More], _Algorithm) -> +do_import(UserGroup, [{UserID, PasswordHash} | More]) + when is_binary(UserID) andalso is_binary(PasswordHash) -> + import_user(UserGroup, UserID, PasswordHash), + do_import(UserGroup, More); +do_import(_UserGroup, [_ | _More]) -> {error, bad_format}; -%% Importing 5w credentials needs 1.7 seconds -do_import(UserGroup, File, Algorithm) -> +%% Importing 5w users needs 1.7 seconds +do_import(UserGroup, File) -> case file:read_line(File) of {ok, Line} -> case binary:split(Line, [<<",">>, <<"\n">>], [global]) of - [UserIdentity, Password, <<>>] -> - add(UserGroup, UserIdentity, Password, Algorithm), - do_import(UserGroup, File, Algorithm); - [UserIdentity, Password] -> - add(UserGroup, UserIdentity, Password, Algorithm), - do_import(UserGroup, File, Algorithm); + [UserID, PasswordHash, <<>>] -> + import_user(UserGroup, UserID, PasswordHash), + do_import(UserGroup, File); + [UserID, PasswordHash] -> + import_user(UserGroup, UserID, PasswordHash), + do_import(UserGroup, File); _ -> {error, bad_format} end; @@ -233,13 +252,18 @@ do_import(UserGroup, File, Algorithm) -> end. -compile({inline, [add/4]}). -add(UserGroup, UserIdentity, Password, Algorithm) -> - Credential = #user_credential{user_identity = {UserGroup, UserIdentity}, - password_hash = emqx_passwd:hash(Algorithm, Password)}, +add(UserGroup, UserID, Password, Algorithm) -> + Credential = #user_info{user_id = {UserGroup, UserID}, + password_hash = emqx_passwd:hash(Algorithm, Password)}, mnesia:write(?TAB, Credential, write). -delete_user_credential(UserCredential) -> - mnesia:delete_object(?TAB, UserCredential, write). +import_user(UserGroup, UserID, PasswordHash) -> + Credential = #user_info{user_id = {UserGroup, UserID}, + password_hash = PasswordHash}, + mnesia:write(?TAB, Credential, write). + +delete_user2(UserInfo) -> + mnesia:delete_object(?TAB, UserInfo, write). %% TODO: Support other type get_user_identity(#{username := Username}, username) -> @@ -259,6 +283,10 @@ trans(Fun, Args) -> end. +to_binary(B) when is_binary(B) -> + B; +to_binary(L) when is_list(L) -> + iolist_to_binary(L). diff --git a/apps/emqx_authentication/test/data/user-credentials.csv b/apps/emqx_authentication/test/data/user-credentials.csv index 7ee4fe8f1..98bd92d52 100644 --- a/apps/emqx_authentication/test/data/user-credentials.csv +++ b/apps/emqx_authentication/test/data/user-credentials.csv @@ -1,2 +1,2 @@ -myuser3,mypassword3 -myuser4,mypassword4 \ No newline at end of file +myuser3,8d41233e39c95b5da13361e354e1c9e639f07b27d397463a8f91b71ee07ccfb2 +myuser4,5809df0154f3cb4ac5c3a5572eaca0c5f7f9d858e887fc675b2becab9feb19d1 \ No newline at end of file diff --git a/apps/emqx_authentication/test/data/user-credentials.json b/apps/emqx_authentication/test/data/user-credentials.json index 6c4689433..a153be616 100644 --- a/apps/emqx_authentication/test/data/user-credentials.json +++ b/apps/emqx_authentication/test/data/user-credentials.json @@ -1,4 +1,4 @@ { - "myuser1": "mypassword1", - "myuser2": "mypassword2" + "myuser1": "09343625c6c123d3434932fe1ce08bae5ac00a8f95bd746e10491b0bafdd1817", + "myuser2": "8767a7d316ad68cb607c7c805b859ffa78277dda13b7a3e2e8b53cad3cabbc6e" } \ No newline at end of file diff --git a/apps/emqx_authentication/test/emqx_authentication_SUITE.erl b/apps/emqx_authentication/test/emqx_authentication_SUITE.erl index 38efe0fe3..d110d940a 100644 --- a/apps/emqx_authentication/test/emqx_authentication_SUITE.erl +++ b/apps/emqx_authentication/test/emqx_authentication_SUITE.erl @@ -37,10 +37,8 @@ end_per_suite(_) -> t_chain(_) -> ChainID = <<"mychain">>, - ChainParams = #{chain_id => ChainID, - services => []}, - ?assertEqual({ok, ChainID}, ?AUTH:create_chain(ChainParams)), - ?assertEqual({error, {already_exists, {chain, ChainID}}}, ?AUTH:create_chain(ChainParams)), + ?assertMatch({ok, #{id := ChainID, services := []}}, ?AUTH:create_chain(#{id => ChainID})), + ?assertEqual({error, {already_exists, {chain, ChainID}}}, ?AUTH:create_chain(#{id => ChainID})), ?assertMatch({ok, #{id := ChainID, services := []}}, ?AUTH:lookup_chain(ChainID)), ?assertEqual(ok, ?AUTH:delete_chain(ChainID)), ?assertMatch({error, {not_found, {chain, ChainID}}}, ?AUTH:lookup_chain(ChainID)), @@ -48,34 +46,33 @@ t_chain(_) -> t_service(_) -> ChainID = <<"mychain">>, + ?assertMatch({ok, #{id := ChainID, services := []}}, ?AUTH:create_chain(#{id => ChainID})), + ?assertMatch({ok, #{id := ChainID, services := []}}, ?AUTH:lookup_chain(ChainID)), + ServiceName1 = <<"myservice1">>, ServiceParams1 = #{name => ServiceName1, type => mnesia, params => #{ - user_identity_type => <<"username">>, + user_id_type => <<"username">>, password_hash_algorithm => <<"sha256">>}}, - ChainParams = #{chain_id => ChainID, - services => [ServiceParams1]}, - ?assertEqual({ok, ChainID}, ?AUTH:create_chain(ChainParams)), - Service1 = ServiceParams1, - ?assertMatch({ok, #{id := ChainID, services := [Service1]}}, ?AUTH:lookup_chain(ChainID)), - ?assertEqual({ok, Service1}, ?AUTH:lookup_service(ChainID, ServiceName1)), - ?assertEqual({ok, [Service1]}, ?AUTH:list_services(ChainID)), + ?assertEqual({ok, [ServiceParams1]}, ?AUTH:add_services(ChainID, [ServiceParams1])), + ?assertEqual({ok, ServiceParams1}, ?AUTH:lookup_service(ChainID, ServiceName1)), + ?assertEqual({ok, [ServiceParams1]}, ?AUTH:list_services(ChainID)), ?assertEqual({error, {already_exists, {service, ServiceName1}}}, ?AUTH:add_services(ChainID, [ServiceParams1])), + ServiceName2 = <<"myservice2">>, ServiceParams2 = ServiceParams1#{name => ServiceName2}, - ?assertEqual(ok, ?AUTH:add_services(ChainID, [ServiceParams2])), - Service2 = ServiceParams2, - ?assertMatch({ok, #{id := ChainID, services := [Service1, Service2]}}, ?AUTH:lookup_chain(ChainID)), - ?assertEqual({ok, Service2}, ?AUTH:lookup_service(ChainID, ServiceName2)), - ?assertEqual({ok, [Service1, Service2]}, ?AUTH:list_services(ChainID)), + ?assertEqual({ok, [ServiceParams2]}, ?AUTH:add_services(ChainID, [ServiceParams2])), + ?assertMatch({ok, #{id := ChainID, services := [ServiceParams1, ServiceParams2]}}, ?AUTH:lookup_chain(ChainID)), + ?assertEqual({ok, ServiceParams2}, ?AUTH:lookup_service(ChainID, ServiceName2)), + ?assertEqual({ok, [ServiceParams1, ServiceParams2]}, ?AUTH:list_services(ChainID)), ?assertEqual(ok, ?AUTH:move_service_to_the_front(ChainID, ServiceName2)), - ?assertEqual({ok, [Service2, Service1]}, ?AUTH:list_services(ChainID)), + ?assertEqual({ok, [ServiceParams2, ServiceParams1]}, ?AUTH:list_services(ChainID)), ?assertEqual(ok, ?AUTH:move_service_to_the_end(ChainID, ServiceName2)), - ?assertEqual({ok, [Service1, Service2]}, ?AUTH:list_services(ChainID)), + ?assertEqual({ok, [ServiceParams1, ServiceParams2]}, ?AUTH:list_services(ChainID)), ?assertEqual(ok, ?AUTH:move_service_to_the_nth(ChainID, ServiceName2, 1)), - ?assertEqual({ok, [Service2, Service1]}, ?AUTH:list_services(ChainID)), + ?assertEqual({ok, [ServiceParams2, ServiceParams1]}, ?AUTH:list_services(ChainID)), ?assertEqual({error, out_of_range}, ?AUTH:move_service_to_the_nth(ChainID, ServiceName2, 3)), ?assertEqual({error, out_of_range}, ?AUTH:move_service_to_the_nth(ChainID, ServiceName2, 0)), ?assertEqual(ok, ?AUTH:delete_services(ChainID, [ServiceName1, ServiceName2])), @@ -85,40 +82,40 @@ t_service(_) -> t_mnesia_service(_) -> ChainID = <<"mychain">>, + ?assertMatch({ok, #{id := ChainID, services := []}}, ?AUTH:create_chain(#{id => ChainID})), + ServiceName = <<"myservice">>, ServiceParams = #{name => ServiceName, type => mnesia, params => #{ - user_identity_type => <<"username">>, + user_id_type => <<"username">>, password_hash_algorithm => <<"sha256">>}}, - ChainParams = #{chain_id => ChainID, - services => [ServiceParams]}, - ?assertEqual({ok, ChainID}, ?AUTH:create_chain(ChainParams)), - UserCredential = #{user_identity => <<"myuser">>, - password => <<"mypass">>}, - ?assertEqual(ok, ?AUTH:add_user_credential(ChainID, ServiceName, UserCredential)), - ?assertMatch({ok, #{user_identity := <<"myuser">>, password_hash := _}}, - ?AUTH:lookup_user_credential(ChainID, ServiceName, <<"myuser">>)), + ?assertEqual({ok, [ServiceParams]}, ?AUTH:add_services(ChainID, [ServiceParams])), + + UserInfo = #{<<"user_id">> => <<"myuser">>, + <<"password">> => <<"mypass">>}, + ?assertEqual({ok, #{user_id => <<"myuser">>}}, ?AUTH:add_user(ChainID, ServiceName, UserInfo)), + ?assertEqual({ok, #{user_id => <<"myuser">>}}, ?AUTH:lookup_user(ChainID, ServiceName, <<"myuser">>)), ClientInfo = #{chain_id => ChainID, username => <<"myuser">>, password => <<"mypass">>}, ?assertEqual(ok, ?AUTH:authenticate(ClientInfo)), ClientInfo2 = ClientInfo#{username => <<"baduser">>}, - ?assertEqual({error, user_credential_not_found}, ?AUTH:authenticate(ClientInfo2)), + ?assertEqual({error, user_not_found}, ?AUTH:authenticate(ClientInfo2)), ClientInfo3 = ClientInfo#{password => <<"badpass">>}, ?assertEqual({error, bad_password}, ?AUTH:authenticate(ClientInfo3)), - UserCredential2 = UserCredential#{password => <<"mypass2">>}, - ?assertEqual(ok, ?AUTH:update_user_credential(ChainID, ServiceName, UserCredential2)), + UserInfo2 = UserInfo#{<<"password">> => <<"mypass2">>}, + ?assertEqual({ok, #{user_id => <<"myuser">>}}, ?AUTH:update_user(ChainID, ServiceName, <<"myuser">>, UserInfo2)), ClientInfo4 = ClientInfo#{password => <<"mypass2">>}, ?assertEqual(ok, ?AUTH:authenticate(ClientInfo4)), - ?assertEqual(ok, ?AUTH:delete_user_credential(ChainID, ServiceName, <<"myuser">>)), - ?assertEqual({error, not_found}, ?AUTH:lookup_user_credential(ChainID, ServiceName, <<"myuser">>)), + ?assertEqual(ok, ?AUTH:delete_user(ChainID, ServiceName, <<"myuser">>)), + ?assertEqual({error, not_found}, ?AUTH:lookup_user(ChainID, ServiceName, <<"myuser">>)), - ?assertEqual(ok, ?AUTH:add_user_credential(ChainID, ServiceName, UserCredential)), - ?assertMatch({ok, #{user_identity := <<"myuser">>}}, ?AUTH:lookup_user_credential(ChainID, ServiceName, <<"myuser">>)), + ?assertEqual({ok, #{user_id => <<"myuser">>}}, ?AUTH:add_user(ChainID, ServiceName, UserInfo)), + ?assertMatch({ok, #{user_id := <<"myuser">>}}, ?AUTH:lookup_user(ChainID, ServiceName, <<"myuser">>)), ?assertEqual(ok, ?AUTH:delete_services(ChainID, [ServiceName])), - ?assertEqual(ok, ?AUTH:add_services(ChainID, [ServiceParams])), - ?assertMatch({error, not_found}, ?AUTH:lookup_user_credential(ChainID, ServiceName, <<"myuser">>)), + ?assertEqual({ok, [ServiceParams]}, ?AUTH:add_services(ChainID, [ServiceParams])), + ?assertMatch({error, not_found}, ?AUTH:lookup_user(ChainID, ServiceName, <<"myuser">>)), ?assertEqual(ok, ?AUTH:delete_chain(ChainID)), ?assertEqual([], ets:tab2list(mnesia_basic_auth)), @@ -126,20 +123,21 @@ t_mnesia_service(_) -> t_import(_) -> ChainID = <<"mychain">>, + ?assertMatch({ok, #{id := ChainID, services := []}}, ?AUTH:create_chain(#{id => ChainID})), + ServiceName = <<"myservice">>, ServiceParams = #{name => ServiceName, type => mnesia, params => #{ - user_identity_type => <<"username">>, + user_id_type => <<"username">>, password_hash_algorithm => <<"sha256">>}}, - ChainParams = #{chain_id => ChainID, - services => [ServiceParams]}, - ?assertEqual({ok, ChainID}, ?AUTH:create_chain(ChainParams)), + ?assertEqual({ok, [ServiceParams]}, ?AUTH:add_services(ChainID, [ServiceParams])), + Dir = code:lib_dir(emqx_authentication, test), - ?assertEqual(ok, ?AUTH:import_user_credentials(ChainID, ServiceName, filename:join([Dir, "data/user-credentials.json"]), json)), - ?assertEqual(ok, ?AUTH:import_user_credentials(ChainID, ServiceName, filename:join([Dir, "data/user-credentials.csv"]), csv)), - ?assertMatch({ok, #{user_identity := <<"myuser1">>}}, ?AUTH:lookup_user_credential(ChainID, ServiceName, <<"myuser1">>)), - ?assertMatch({ok, #{user_identity := <<"myuser3">>}}, ?AUTH:lookup_user_credential(ChainID, ServiceName, <<"myuser3">>)), + ?assertEqual(ok, ?AUTH:import_users(ChainID, ServiceName, filename:join([Dir, "data/user-credentials.json"]))), + ?assertEqual(ok, ?AUTH:import_users(ChainID, ServiceName, filename:join([Dir, "data/user-credentials.csv"]))), + ?assertMatch({ok, #{user_id := <<"myuser1">>}}, ?AUTH:lookup_user(ChainID, ServiceName, <<"myuser1">>)), + ?assertMatch({ok, #{user_id := <<"myuser3">>}}, ?AUTH:lookup_user(ChainID, ServiceName, <<"myuser3">>)), ClientInfo1 = #{chain_id => ChainID, username => <<"myuser1">>, password => <<"mypassword1">>}, @@ -152,30 +150,31 @@ t_import(_) -> t_multi_mnesia_service(_) -> ChainID = <<"mychain">>, + ?assertMatch({ok, #{id := ChainID, services := []}}, ?AUTH:create_chain(#{id => ChainID})), + ServiceName1 = <<"myservice1">>, ServiceParams1 = #{name => ServiceName1, type => mnesia, params => #{ - user_identity_type => <<"username">>, + user_id_type => <<"username">>, password_hash_algorithm => <<"sha256">>}}, ServiceName2 = <<"myservice2">>, ServiceParams2 = #{name => ServiceName2, type => mnesia, params => #{ - user_identity_type => <<"clientid">>, + user_id_type => <<"clientid">>, password_hash_algorithm => <<"sha256">>}}, - ChainParams = #{chain_id => ChainID, - services => [ServiceParams1, ServiceParams2]}, - ?assertEqual({ok, ChainID}, ?AUTH:create_chain(ChainParams)), + ?assertEqual({ok, [ServiceParams1]}, ?AUTH:add_services(ChainID, [ServiceParams1])), + ?assertEqual({ok, [ServiceParams2]}, ?AUTH:add_services(ChainID, [ServiceParams2])), - ?assertEqual(ok, ?AUTH:add_user_credential(ChainID, - ServiceName1, - #{user_identity => <<"myuser">>, - password => <<"mypass1">>})), - ?assertEqual(ok, ?AUTH:add_user_credential(ChainID, - ServiceName2, - #{user_identity => <<"myclient">>, - password => <<"mypass2">>})), + ?assertEqual({ok, #{user_id => <<"myuser">>}}, + ?AUTH:add_user(ChainID, ServiceName1, + #{<<"user_id">> => <<"myuser">>, + <<"password">> => <<"mypass1">>})), + ?assertEqual({ok, #{user_id => <<"myclient">>}}, + ?AUTH:add_user(ChainID, ServiceName2, + #{<<"user_id">> => <<"myclient">>, + <<"password">> => <<"mypass2">>})), ClientInfo1 = #{chain_id => ChainID, username => <<"myuser">>, clientid => <<"myclient">>,