diff --git a/apps/emqx_dashboard/src/emqx_dashboard_token.erl b/apps/emqx_dashboard/src/emqx_dashboard_token.erl index 1c840e90c..9a9875935 100644 --- a/apps/emqx_dashboard/src/emqx_dashboard_token.erl +++ b/apps/emqx_dashboard/src/emqx_dashboard_token.erl @@ -248,8 +248,8 @@ clean_expired_jwt(Now) -> -if(?EMQX_RELEASE_EDITION == ee). check_rbac(Req, JWT) -> - #?ADMIN_JWT{exptime = _ExpTime, extra = Extra, username = _Username} = JWT, - case emqx_dashboard_rbac:check_rbac(Req, Extra) of + #?ADMIN_JWT{exptime = _ExpTime, extra = Extra, username = Username} = JWT, + case emqx_dashboard_rbac:check_rbac(Req, Username, Extra) of true -> save_new_jwt(JWT); _ -> diff --git a/apps/emqx_dashboard_rbac/src/emqx_dashboard_rbac.app.src b/apps/emqx_dashboard_rbac/src/emqx_dashboard_rbac.app.src index 190764e2f..ec8e6cd3f 100644 --- a/apps/emqx_dashboard_rbac/src/emqx_dashboard_rbac.app.src +++ b/apps/emqx_dashboard_rbac/src/emqx_dashboard_rbac.app.src @@ -1,6 +1,6 @@ {application, emqx_dashboard_rbac, [ {description, "EMQX Dashboard RBAC"}, - {vsn, "0.1.0"}, + {vsn, "0.1.1"}, {registered, []}, {applications, [ kernel, diff --git a/apps/emqx_dashboard_rbac/src/emqx_dashboard_rbac.erl b/apps/emqx_dashboard_rbac/src/emqx_dashboard_rbac.erl index 28bd8960e..57132b65b 100644 --- a/apps/emqx_dashboard_rbac/src/emqx_dashboard_rbac.erl +++ b/apps/emqx_dashboard_rbac/src/emqx_dashboard_rbac.erl @@ -6,18 +6,18 @@ -include_lib("emqx_dashboard/include/emqx_dashboard.hrl"). --export([check_rbac/2, role/1, valid_role/1]). +-export([check_rbac/3, role/1, valid_role/1]). -dialyzer({nowarn_function, role/1}). %%===================================================================== %% API -check_rbac(Req, Extra) -> +check_rbac(Req, Username, Extra) -> Role = role(Extra), Method = cowboy_req:method(Req), AbsPath = cowboy_req:path(Req), case emqx_dashboard_swagger:get_relative_uri(AbsPath) of {ok, Path} -> - check_rbac(Role, Method, Path); + check_rbac(Role, Method, Path, Username); _ -> false end. @@ -41,14 +41,21 @@ valid_role(Role) -> {error, <<"Role does not exist">>} end. %% =================================================================== -check_rbac(?ROLE_SUPERUSER, _, _) -> +check_rbac(?ROLE_SUPERUSER, _, _, _) -> true; -check_rbac(?ROLE_VIEWER, <<"GET">>, _) -> +check_rbac(?ROLE_VIEWER, <<"GET">>, _, _) -> true; -%% this API is a special case -check_rbac(?ROLE_VIEWER, <<"POST">>, <<"/logout">>) -> +%% everyone should allow to logout +check_rbac(?ROLE_VIEWER, <<"POST">>, <<"/logout">>, _) -> true; -check_rbac(_, _, _) -> +%% viewer should allow to change self password, +%% superuser should allow to change any user +check_rbac(?ROLE_VIEWER, <<"POST">>, <<"/users/", SubPath/binary>>, Username) -> + case binary:split(SubPath, <<"/">>, [global]) of + [Username, <<"change_pwd">>] -> true; + _ -> false + end; +check_rbac(_, _, _, _) -> false. role_list() -> diff --git a/apps/emqx_dashboard_rbac/test/emqx_dashboard_rbac_SUITE.erl b/apps/emqx_dashboard_rbac/test/emqx_dashboard_rbac_SUITE.erl index b1a51a3c9..eeac8dadf 100644 --- a/apps/emqx_dashboard_rbac/test/emqx_dashboard_rbac_SUITE.erl +++ b/apps/emqx_dashboard_rbac/test/emqx_dashboard_rbac_SUITE.erl @@ -160,6 +160,34 @@ t_login_out(_) -> {ok, Username} = emqx_dashboard_admin:verify_token(FakeReq, Token), ok. +t_change_pwd(_) -> + Viewer1 = <<"viewer1">>, + Viewer2 = <<"viewer2">>, + SuperUser = <<"super_user">>, + Password = <<"public_www1">>, + Desc = <<"desc">>, + {ok, _} = emqx_dashboard_admin:add_user(Viewer1, Password, ?ROLE_VIEWER, Desc), + {ok, _} = emqx_dashboard_admin:add_user(Viewer2, Password, ?ROLE_VIEWER, Desc), + {ok, _} = emqx_dashboard_admin:add_user(SuperUser, Password, ?ROLE_SUPERUSER, Desc), + {ok, ?ROLE_VIEWER, Viewer1Token} = emqx_dashboard_admin:sign_token(Viewer1, Password), + {ok, ?ROLE_SUPERUSER, SuperToken} = emqx_dashboard_admin:sign_token(SuperUser, Password), + %% viewer can change own password + ?assertEqual({ok, Viewer1}, change_pwd(Viewer1Token, Viewer1)), + %% viewer can't change other's password + ?assertEqual({error, unauthorized_role}, change_pwd(Viewer1Token, Viewer2)), + ?assertEqual({error, unauthorized_role}, change_pwd(Viewer1Token, SuperUser)), + %% superuser can change other's password + ?assertEqual({ok, SuperUser}, change_pwd(SuperToken, Viewer1)), + ?assertEqual({ok, SuperUser}, change_pwd(SuperToken, Viewer2)), + ?assertEqual({ok, SuperUser}, change_pwd(SuperToken, SuperUser)), + ok. + +change_pwd(Token, Username) -> + Path = "/users/" ++ binary_to_list(Username) ++ "/change_pwd", + Path1 = erlang:list_to_binary(emqx_dashboard_swagger:relative_uri(Path)), + Req = #{method => <<"POST">>, path => Path1}, + emqx_dashboard_admin:verify_token(Req, Token). + add_default_superuser() -> {ok, _NewUser} = emqx_dashboard_admin:add_user( ?DEFAULT_SUPERUSER, diff --git a/changes/ce/feat-11785.en.md b/changes/ce/feat-11785.en.md new file mode 100644 index 000000000..765ce6ea0 --- /dev/null +++ b/changes/ce/feat-11785.en.md @@ -0,0 +1 @@ +Allow viewer to change their own passwords, viewer can't change other's password.