From cc4805b1ac9337c599c0f3daec7c88d0a5912daa Mon Sep 17 00:00:00 2001 From: zmstone Date: Tue, 19 Mar 2024 19:53:16 +0100 Subject: [PATCH] feat: extract attrs field from http and jwt auth response --- apps/emqx/src/emqx_channel.erl | 16 ++++++++++++++-- apps/emqx_auth_http/src/emqx_authn_http.erl | 7 ++++--- .../test/emqx_authn_http_SUITE.erl | 12 ++++++------ apps/emqx_auth_jwt/src/emqx_auth_jwt.app.src | 2 +- apps/emqx_auth_jwt/src/emqx_authn_jwt.erl | 5 ++++- 5 files changed, 29 insertions(+), 13 deletions(-) diff --git a/apps/emqx/src/emqx_channel.erl b/apps/emqx/src/emqx_channel.erl index 1f51c15b1..5ca26ba4e 100644 --- a/apps/emqx/src/emqx_channel.erl +++ b/apps/emqx/src/emqx_channel.erl @@ -1726,9 +1726,21 @@ do_authenticate(Credential, #channel{clientinfo = ClientInfo} = Channel) -> {error, emqx_reason_codes:connack_error(Reason)} end. -merge_auth_result(ClientInfo, AuthResult) when is_map(ClientInfo) andalso is_map(AuthResult) -> +%% Merge authentication result into ClientInfo +%% Authentication result may include: +%% 1. `is_superuser': The superuser flag from various backends +%% 2. `acl': ACL rules from JWT, HTTP auth backend +%% 3. `attrs': Extra client attributes from JWT, HTTP auth backend +merge_auth_result(ClientInfo, AuthResult0) when is_map(ClientInfo) andalso is_map(AuthResult0) -> IsSuperuser = maps:get(is_superuser, AuthResult, false), - maps:merge(ClientInfo, AuthResult#{is_superuser => IsSuperuser}). + AuthResult = maps:without([attrs], AuthResult0), + Attrs0 = maps:get(attrs, ClientInfo, #{}), + Attrs1 = maps:get(attrs, AuthResult0, #{}), + Attrs = maps:merge(Attrs0, Attrs1), + maps:merge( + ClientInfo#{attrs => Attrs}, + AuthResult#{is_superuser => IsSuperuser} + ). %%-------------------------------------------------------------------- %% Process Topic Alias diff --git a/apps/emqx_auth_http/src/emqx_authn_http.erl b/apps/emqx_auth_http/src/emqx_authn_http.erl index 6e8bf0514..4efa69042 100644 --- a/apps/emqx_auth_http/src/emqx_authn_http.erl +++ b/apps/emqx_auth_http/src/emqx_authn_http.erl @@ -197,9 +197,10 @@ handle_response(Headers, Body) -> {ok, NBody} -> case maps:get(<<"result">>, NBody, <<"ignore">>) of <<"allow">> -> - Res = emqx_authn_utils:is_superuser(NBody), - %% TODO: Return by user property - {ok, Res#{user_property => maps:get(<<"user_property">>, NBody, #{})}}; + IsSuperuser = emqx_authn_utils:is_superuser(NBody), + Attrs = maps:get(<<"attrs">>, NBody, #{}), + Result = maps:merge(IsSuperuser, Attrs), + {ok, Result}; <<"deny">> -> {error, not_authorized}; <<"ignore">> -> diff --git a/apps/emqx_auth_http/test/emqx_authn_http_SUITE.erl b/apps/emqx_auth_http/test/emqx_authn_http_SUITE.erl index 3be69641b..12211bab8 100644 --- a/apps/emqx_auth_http/test/emqx_authn_http_SUITE.erl +++ b/apps/emqx_auth_http/test/emqx_authn_http_SUITE.erl @@ -533,7 +533,7 @@ samples() -> {ok, Req, State} end, config_params => #{}, - result => {ok, #{is_superuser => false, user_property => #{}}} + result => {ok, #{is_superuser => false, attrs => #{}}} }, %% get request with json body response @@ -548,7 +548,7 @@ samples() -> {ok, Req, State} end, config_params => #{}, - result => {ok, #{is_superuser => true, user_property => #{}}} + result => {ok, #{is_superuser => true, attrs => #{}}} }, %% get request with url-form-encoded body response @@ -566,7 +566,7 @@ samples() -> {ok, Req, State} end, config_params => #{}, - result => {ok, #{is_superuser => true, user_property => #{}}} + result => {ok, #{is_superuser => true, attrs => #{}}} }, %% get request with response of unknown encoding @@ -608,7 +608,7 @@ samples() -> <<"method">> => <<"post">>, <<"headers">> => #{<<"content-type">> => <<"application/json">>} }, - result => {ok, #{is_superuser => false, user_property => #{}}} + result => {ok, #{is_superuser => false, attrs => #{}}} }, %% simple post request, application/x-www-form-urlencoded @@ -634,7 +634,7 @@ samples() -> <<"application/x-www-form-urlencoded">> } }, - result => {ok, #{is_superuser => false, user_property => #{}}} + result => {ok, #{is_superuser => false, attrs => #{}}} }, %% simple post request for placeholders, application/json @@ -669,7 +669,7 @@ samples() -> <<"cert_common_name">> => ?PH_CERT_CN_NAME } }, - result => {ok, #{is_superuser => false, user_property => #{}}} + result => {ok, #{is_superuser => false, attrs => #{}}} }, %% custom headers diff --git a/apps/emqx_auth_jwt/src/emqx_auth_jwt.app.src b/apps/emqx_auth_jwt/src/emqx_auth_jwt.app.src index 7e313881e..62d560d2f 100644 --- a/apps/emqx_auth_jwt/src/emqx_auth_jwt.app.src +++ b/apps/emqx_auth_jwt/src/emqx_auth_jwt.app.src @@ -1,7 +1,7 @@ %% -*- mode: erlang -*- {application, emqx_auth_jwt, [ {description, "EMQX JWT Authentication and Authorization"}, - {vsn, "0.2.0"}, + {vsn, "0.3.0"}, {registered, []}, {mod, {emqx_auth_jwt_app, []}}, {applications, [ diff --git a/apps/emqx_auth_jwt/src/emqx_authn_jwt.erl b/apps/emqx_auth_jwt/src/emqx_authn_jwt.erl index 5f89d076d..45fd1d60a 100644 --- a/apps/emqx_auth_jwt/src/emqx_authn_jwt.erl +++ b/apps/emqx_auth_jwt/src/emqx_authn_jwt.erl @@ -220,7 +220,10 @@ verify(JWT, JWKs, VerifyClaims, AclClaimName) -> case do_verify(JWT, JWKs, VerifyClaims) of {ok, Extra} -> try - {ok, acl(Extra, AclClaimName)} + ACL = acl(Extra, AclClaimName), + Attrs = maps:get(<<"attrs">>, Extra, #{}), + Result = maps:merge(Attrs, ACL), + {ok, Result} catch throw:{bad_acl_rule, Reason} -> %% it's a invalid token, so ok to log