From 2283db62ec7fe1a0ece21932866fb0cbd2bb48b0 Mon Sep 17 00:00:00 2001 From: firest Date: Thu, 21 Sep 2023 11:52:30 +0800 Subject: [PATCH] fix(sso): add a API with authorization-free to list running backends --- apps/emqx_dashboard/src/emqx_dashboard.erl | 27 ++++++++++-- .../src/emqx_dashboard_sso_api.erl | 43 +++++++++++++++++-- .../test/emqx_dashboard_sso_ldap_SUITE.erl | 19 +++++++- rel/i18n/emqx_dashboard_sso_api.hocon | 5 +++ 4 files changed, 85 insertions(+), 9 deletions(-) diff --git a/apps/emqx_dashboard/src/emqx_dashboard.erl b/apps/emqx_dashboard/src/emqx_dashboard.erl index e6a9a2fd1..9599eb567 100644 --- a/apps/emqx_dashboard/src/emqx_dashboard.erl +++ b/apps/emqx_dashboard/src/emqx_dashboard.erl @@ -217,12 +217,31 @@ authorize(Req) -> <<"You don't have permission to access this resource">>} end; _ -> - return_unauthorized( - <<"AUTHORIZATION_HEADER_ERROR">>, - <<"Support authorization: basic/bearer ">> - ) + case is_authorization_free(Req) of + true -> + ok; + _ -> + return_unauthorized( + <<"AUTHORIZATION_HEADER_ERROR">>, + <<"Support authorization: basic/bearer ">> + ) + end end. +-if(?EMQX_RELEASE_EDITION == ee). +%% this is a temporary design to skip the authorization for some APIs, +%% it will be removed future +is_authorization_free(Req) -> + emqx_dashboard_sso_api:is_authorization_free(Req). + +-else. + +-dialyzer({no_match, [authorize/1]}). + +is_authorization_free(_Req) -> + false. +-endif. + return_unauthorized(Code, Message) -> {401, #{ diff --git a/apps/emqx_dashboard_sso/src/emqx_dashboard_sso_api.erl b/apps/emqx_dashboard_sso/src/emqx_dashboard_sso_api.erl index c163c3e8a..9e348e4d2 100644 --- a/apps/emqx_dashboard_sso/src/emqx_dashboard_sso_api.erl +++ b/apps/emqx_dashboard_sso/src/emqx_dashboard_sso_api.erl @@ -25,12 +25,13 @@ ]). -export([ + running/2, login/2, sso/2, backend/2 ]). --export([sso_parameters/1]). +-export([sso_parameters/1, is_authorization_free/1]). -define(BAD_USERNAME_OR_PWD, 'BAD_USERNAME_OR_PWD'). -define(BAD_REQUEST, 'BAD_REQUEST'). @@ -45,10 +46,22 @@ api_spec() -> paths() -> [ "/sso", - "/sso/login/:backend", - "/sso/:backend" + "/sso/:backend", + "/sso/running", + "/sso/login/:backend" ]. +schema("/sso/running") -> + #{ + 'operationId' => running, + get => #{ + tags => [?TAGS], + desc => ?DESC(list_running), + responses => #{ + 200 => array(enum(emqx_dashboard_sso:types())) + } + } + }; schema("/sso") -> #{ 'operationId' => sso, @@ -113,6 +126,19 @@ fields(backend_status) -> %% ------------------------------------------------------------------------------------------------- %% API +running(get, _Request) -> + SSO = emqx:get_config([dashboard_sso], #{}), + {200, + lists:filtermap( + fun + (#{backend := Backend, enable := true}) -> + {true, Backend}; + (_) -> + false + end, + maps:values(SSO) + )}. + login(post, #{bindings := #{backend := Backend}, body := Sign}) -> case emqx_dashboard_sso_manager:lookup_state(Backend) of undefined -> @@ -165,6 +191,10 @@ backend(delete, #{bindings := #{backend := Backend}}) -> sso_parameters(Params) -> backend_name_as_arg(query, [local], <<"local">>) ++ Params. +is_authorization_free(Req) -> + Path = cowboy_req:path(Req), + is_path_authorization_free(Path). + %% ------------------------------------------------------------------------------------------------- %% internal response_schema(401) -> @@ -225,3 +255,10 @@ to_json(Data) -> {K, emqx_utils_maps:binary_string(V)} end ). + +is_path_authorization_free(<<"/api/v5/sso/running">>) -> + true; +is_path_authorization_free(<<"/api/v5/sso/login", _/binary>>) -> + true; +is_path_authorization_free(_) -> + false. diff --git a/apps/emqx_dashboard_sso/test/emqx_dashboard_sso_ldap_SUITE.erl b/apps/emqx_dashboard_sso/test/emqx_dashboard_sso_ldap_SUITE.erl index 5166aa4c5..605be7fd1 100644 --- a/apps/emqx_dashboard_sso/test/emqx_dashboard_sso_ldap_SUITE.erl +++ b/apps/emqx_dashboard_sso/test/emqx_dashboard_sso_ldap_SUITE.erl @@ -14,7 +14,7 @@ -define(LDAP_DEFAULT_PORT, 389). -define(LDAP_USER, <<"mqttuser0001">>). -define(LDAP_USER_PASSWORD, <<"mqttuser0001">>). --import(emqx_mgmt_api_test_util, [request/2, request/3, uri/1]). +-import(emqx_mgmt_api_test_util, [request/2, request/3, uri/1, request_api/3]). all() -> [ @@ -54,8 +54,10 @@ end_per_testcase(Case, _) -> ok. t_create(_) -> + check_running([]), Path = uri(["sso", "ldap"]), {ok, 200, Result} = request(put, Path, ldap_config()), + check_running([]), ?assertMatch(#{backend := <<"ldap">>, enable := false}, decode_json(Result)), ?assertMatch([#{backend := <<"ldap">>, enable := false}], get_sso()), ?assertNotEqual(undefined, emqx_dashboard_sso_manager:lookup_state(ldap)), @@ -64,6 +66,7 @@ t_create(_) -> t_update(_) -> Path = uri(["sso", "ldap"]), {ok, 200, Result} = request(put, Path, ldap_config(#{<<"enable">> => <<"true">>})), + check_running([<<"ldap">>]), ?assertMatch(#{backend := <<"ldap">>, enable := true}, decode_json(Result)), ?assertMatch([#{backend := <<"ldap">>, enable := true}], get_sso()), ?assertNotEqual(undefined, emqx_dashboard_sso_manager:lookup_state(ldap)), @@ -96,7 +99,8 @@ t_first_login(_) -> <<"username">> => ?LDAP_USER, <<"password">> => ?LDAP_USER_PASSWORD }, - {ok, 200, Result} = request(post, Path, Req), + %% this API is authorization-free + {ok, 200, Result} = request_without_authorization(post, Path, Req), ?assertMatch(#{license := _, token := _}, decode_json(Result)), ?assertMatch( [#?ADMIN{username = ?SSO_USERNAME(ldap, ?LDAP_USER)}], @@ -119,8 +123,15 @@ t_delete(_) -> Path = uri(["sso", "ldap"]), ?assertMatch({ok, 204, _}, request(delete, Path)), ?assertMatch({ok, 404, _}, request(delete, Path)), + check_running([]), ok. +check_running(Expect) -> + Path = uri(["sso", "running"]), + %% this API is authorization-free + {ok, Result} = request_api(get, Path, []), + ?assertEqual(Expect, decode_json(Result)). + get_sso() -> Path = uri(["sso"]), {ok, 200, Result} = request(get, Path), @@ -150,3 +161,7 @@ ldap_server() -> decode_json(Data) -> BinJson = emqx_utils_json:decode(Data, [return_maps]), emqx_utils_maps:unsafe_atom_key_map(BinJson). + +request_without_authorization(Method, Url, Body) -> + Opts = #{compatible_mode => true, httpc_req_opts => [{body_format, binary}]}, + emqx_mgmt_api_test_util:request_api(Method, Url, [], [], Body, Opts). diff --git a/rel/i18n/emqx_dashboard_sso_api.hocon b/rel/i18n/emqx_dashboard_sso_api.hocon index 85cb7d31b..e14039156 100644 --- a/rel/i18n/emqx_dashboard_sso_api.hocon +++ b/rel/i18n/emqx_dashboard_sso_api.hocon @@ -1,5 +1,10 @@ emqx_dashboard_api { +list_running.desc: +"""List all running SSO backends""" +list_running.label: +"""Running Backends""" + get_sso.desc: """List all SSO backends""" get_sso.label: