From 3d398873f195bd29105d078aed3c778eeb7e4ad4 Mon Sep 17 00:00:00 2001 From: firest Date: Wed, 26 Jun 2024 15:49:13 +0800 Subject: [PATCH] fix(oidc): return to dashboard when provider calls back fixed a bug when updating config --- .../src/emqx_dashboard_sso_api.erl | 9 ++-- .../src/emqx_dashboard_sso_manager.erl | 9 +++- .../src/emqx_dashboard_sso_oidc.erl | 9 ++-- .../src/emqx_dashboard_sso_oidc_api.erl | 51 ++++++++++++------- .../src/emqx_dashboard_sso_oidc_session.erl | 3 +- .../src/emqx_dashboard_sso_saml.erl | 2 +- 6 files changed, 54 insertions(+), 29 deletions(-) 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 20bc99de1..81abc8f19 100644 --- a/apps/emqx_dashboard_sso/src/emqx_dashboard_sso_api.erl +++ b/apps/emqx_dashboard_sso/src/emqx_dashboard_sso_api.erl @@ -33,7 +33,7 @@ backend/2 ]). --export([sso_parameters/1, login_meta/3]). +-export([sso_parameters/1, login_meta/4]). -define(REDIRECT, 'REDIRECT'). -define(BAD_USERNAME_OR_PWD, 'BAD_USERNAME_OR_PWD'). @@ -168,7 +168,7 @@ login(post, #{bindings := #{backend := Backend}, body := Body} = Request) -> request => emqx_utils:redact(Request) }), Username = maps:get(<<"username">>, Body), - {200, login_meta(Username, Role, Token)}; + {200, login_meta(Username, Role, Token, Backend)}; {redirect, Redirect} -> ?SLOG(info, #{ msg => "dashboard_sso_login_redirect", @@ -286,11 +286,12 @@ to_redacted_json(Data) -> end ). -login_meta(Username, Role, Token) -> +login_meta(Username, Role, Token, Backend) -> #{ username => Username, role => Role, token => Token, version => iolist_to_binary(proplists:get_value(version, emqx_sys:info())), - license => #{edition => emqx_release:edition()} + license => #{edition => emqx_release:edition()}, + backend => Backend }. diff --git a/apps/emqx_dashboard_sso/src/emqx_dashboard_sso_manager.erl b/apps/emqx_dashboard_sso/src/emqx_dashboard_sso_manager.erl index c1d450ef2..6834da9e9 100644 --- a/apps/emqx_dashboard_sso/src/emqx_dashboard_sso_manager.erl +++ b/apps/emqx_dashboard_sso/src/emqx_dashboard_sso_manager.erl @@ -107,7 +107,14 @@ get_backend_status(Backend, _) -> end. update(Backend, Config) -> - update_config(Backend, {?FUNCTION_NAME, Backend, Config}). + UpdateConf = + case emqx:get_raw_config(?MOD_KEY_PATH(Backend), #{}) of + RawConf when is_map(RawConf) -> + emqx_utils:deobfuscate(Config, RawConf); + null -> + Config + end, + update_config(Backend, {?FUNCTION_NAME, Backend, UpdateConf}). delete(Backend) -> update_config(Backend, {?FUNCTION_NAME, Backend}). diff --git a/apps/emqx_dashboard_sso/src/emqx_dashboard_sso_oidc.erl b/apps/emqx_dashboard_sso/src/emqx_dashboard_sso_oidc.erl index 1a4562336..dbc0d7f0b 100644 --- a/apps/emqx_dashboard_sso/src/emqx_dashboard_sso_oidc.erl +++ b/apps/emqx_dashboard_sso/src/emqx_dashboard_sso_oidc.erl @@ -62,9 +62,8 @@ fields(oidc) -> #{desc => ?DESC(clientid), required => true} )}, {secret, - ?HOCON( - binary(), - #{desc => ?DESC(secret), required => true} + emqx_schema_secret:mk( + maps:merge(#{desc => ?DESC(secret), required => true}, #{}) )}, {scopes, ?HOCON( @@ -82,7 +81,7 @@ fields(oidc) -> default => <<"http://127.0.0.1:18083">> })}, {session_expiry, - ?HOCON(emqx_schema:timeout_duration_ms(), #{ + ?HOCON(emqx_schema:timeout_duration_s(), #{ desc => ?DESC(session_expiry), default => <<"30s">> })}, @@ -217,7 +216,7 @@ login( oidcc:create_redirect_url( ?PROVIDER_SVR_NAME, ClientId, - Secret, + emqx_secret:unwrap(Secret), Opts#{ state => State, client_jwks => ClientJwks, diff --git a/apps/emqx_dashboard_sso/src/emqx_dashboard_sso_oidc_api.erl b/apps/emqx_dashboard_sso/src/emqx_dashboard_sso_oidc_api.erl index 3b5c9f5d8..3514b4fbb 100644 --- a/apps/emqx_dashboard_sso/src/emqx_dashboard_sso_oidc_api.erl +++ b/apps/emqx_dashboard_sso/src/emqx_dashboard_sso_oidc_api.erl @@ -30,6 +30,14 @@ -define(BAD_USERNAME_OR_PWD, 'BAD_USERNAME_OR_PWD'). -define(BACKEND_NOT_FOUND, 'BACKEND_NOT_FOUND'). + +-define(RESPHEADERS, #{ + <<"cache-control">> => <<"no-cache">>, + <<"pragma">> => <<"no-cache">>, + <<"content-type">> => <<"text/plain">> +}). +-define(REDIRECT_BODY, <<"Redirecting...">>). + -define(TAGS, <<"Dashboard Single Sign-On">>). -define(BACKEND, oidc). -define(BASE_PATH, "/api/v5"). @@ -66,11 +74,12 @@ schema("/sso/oidc/callback") -> %%-------------------------------------------------------------------- code_callback(get, #{query_string := QS}) -> case ensure_sso_state(QS) of - {ok, Username, Role, DashboardToken} -> + {ok, Target} -> ?SLOG(info, #{ msg => "dashboard_sso_login_successful" }), - {200, login_meta(Username, Role, DashboardToken)}; + + {302, ?RESPHEADERS#{<<"location">> => Target}, ?REDIRECT_BODY}; {error, invalid_backend} -> {404, #{code => ?BACKEND_NOT_FOUND, message => <<"Backend not found">>}}; {error, Reason} -> @@ -130,7 +139,7 @@ retrieve_token( Code, Name, ClientId, - Secret, + emqx_secret:unwrap(Secret), Data#{ redirect_uri => make_callback_url(Cfg), client_jwks => ClientJwks, @@ -144,18 +153,21 @@ retrieve_token( Error end. -retrieve_userinfo(Token, #{ - name := Name, - client_jwks := ClientJwks, - config := #{clientid := ClientId, secret := Secret}, - name_tokens := NameTks -}) -> +retrieve_userinfo( + Token, + #{ + name := Name, + client_jwks := ClientJwks, + config := #{clientid := ClientId, secret := Secret}, + name_tokens := NameTks + } = Cfg +) -> case oidcc:retrieve_userinfo( Token, Name, ClientId, - Secret, + emqx_secret:unwrap(Secret), #{client_jwks => ClientJwks} ) of @@ -165,29 +177,29 @@ retrieve_userinfo(Token, #{ user_info => UserInfo }), Username = emqx_placeholder:proc_tmpl(NameTks, UserInfo), - ensure_user_exists(Username); + ensure_user_exists(Cfg, Username); {error, _Reason} = Error -> Error end. --dialyzer({nowarn_function, ensure_user_exists/1}). -ensure_user_exists(<<>>) -> +-dialyzer({nowarn_function, ensure_user_exists/2}). +ensure_user_exists(_Cfg, <<>>) -> {error, <<"Username can not be empty">>}; -ensure_user_exists(<<"undefined">>) -> +ensure_user_exists(_Cfg, <<"undefined">>) -> {error, <<"Username can not be undefined">>}; -ensure_user_exists(Username) -> +ensure_user_exists(Cfg, Username) -> case emqx_dashboard_admin:lookup_user(?BACKEND, Username) of [User] -> case emqx_dashboard_token:sign(User, <<>>) of {ok, Role, Token} -> - {ok, Username, Role, Token}; + {ok, login_redirect_target(Cfg, Username, Role, Token)}; Error -> Error end; [] -> case emqx_dashboard_admin:add_sso_user(?BACKEND, Username, ?ROLE_VIEWER, <<>>) of {ok, _} -> - ensure_user_exists(Username); + ensure_user_exists(Cfg, Username); Error -> Error end @@ -195,3 +207,8 @@ ensure_user_exists(Username) -> make_callback_url(#{config := #{dashboard_addr := Addr}}) -> list_to_binary(binary_to_list(Addr) ++ ?BASE_PATH ++ ?CALLBACK_PATH). + +login_redirect_target(#{config := #{dashboard_addr := Addr}}, Username, Role, Token) -> + LoginMeta = emqx_dashboard_sso_api:login_meta(Username, Role, Token, oidc), + MetaBin = base64:encode(emqx_utils_json:encode(LoginMeta)), + <>) =:= nomatch). login_redirect_target(DashboardAddr, Username, Role, Token) -> - LoginMeta = emqx_dashboard_sso_api:login_meta(Username, Role, Token), + LoginMeta = emqx_dashboard_sso_api:login_meta(Username, Role, Token, saml), <