feat(dashboard): add RBAC feature for Dashboard
This commit is contained in:
parent
cf334d5542
commit
5c31c5ce76
|
@ -15,23 +15,28 @@
|
|||
%%--------------------------------------------------------------------
|
||||
-define(ADMIN, emqx_admin).
|
||||
|
||||
-define(ROLE_VIEWER, <<"viewer">>).
|
||||
-define(ROLE_DEFAULT, ?ROLE_VIEWER).
|
||||
-define(ROLE_SUPERUSER, <<"superuser">>).
|
||||
|
||||
-record(?ADMIN, {
|
||||
username :: binary(),
|
||||
pwdhash :: binary(),
|
||||
description :: binary(),
|
||||
role = undefined :: atom(),
|
||||
%% not used so far, for future extension
|
||||
extra = [] :: term()
|
||||
role = ?ROLE_DEFAULT :: binary(),
|
||||
extra = #{} :: map()
|
||||
}).
|
||||
|
||||
-type dashboard_user_role() :: binary().
|
||||
-type dashboard_user() :: #?ADMIN{}.
|
||||
|
||||
-define(ADMIN_JWT, emqx_admin_jwt).
|
||||
|
||||
-record(?ADMIN_JWT, {
|
||||
token :: binary(),
|
||||
username :: binary(),
|
||||
exptime :: integer(),
|
||||
%% not used so far, fur future extension
|
||||
extra = [] :: term()
|
||||
extra = #{} :: map()
|
||||
}).
|
||||
|
||||
-define(TAB_COLLECT, emqx_collect).
|
||||
|
|
|
@ -205,13 +205,15 @@ authorize(Req) ->
|
|||
{basic, Username, Password} ->
|
||||
api_key_authorize(Req, Username, Password);
|
||||
{bearer, Token} ->
|
||||
case emqx_dashboard_admin:verify_token(Token) of
|
||||
case emqx_dashboard_admin:verify_token(Req, Token) of
|
||||
ok ->
|
||||
ok;
|
||||
{error, token_timeout} ->
|
||||
{401, 'TOKEN_TIME_OUT', <<"Token expired, get new token by POST /login">>};
|
||||
{error, not_found} ->
|
||||
{401, 'BAD_TOKEN', <<"Get a token by POST /login">>}
|
||||
{401, 'BAD_TOKEN', <<"Get a token by POST /login">>};
|
||||
{error, unauthorized_role} ->
|
||||
{401, 'UNAUTHORIZED_ROLE', <<"Unauthorized Role">>}
|
||||
end;
|
||||
_ ->
|
||||
return_unauthorized(
|
||||
|
|
|
@ -30,10 +30,10 @@
|
|||
-export([mnesia/1]).
|
||||
|
||||
-export([
|
||||
add_user/3,
|
||||
force_add_user/3,
|
||||
add_user/4,
|
||||
force_add_user/4,
|
||||
remove_user/1,
|
||||
update_user/2,
|
||||
update_user/3,
|
||||
lookup_user/1,
|
||||
change_password/2,
|
||||
change_password/3,
|
||||
|
@ -43,7 +43,7 @@
|
|||
|
||||
-export([
|
||||
sign_token/2,
|
||||
verify_token/1,
|
||||
verify_token/2,
|
||||
destroy_token_by_username/2
|
||||
]).
|
||||
-export([
|
||||
|
@ -98,18 +98,19 @@ add_default_user() ->
|
|||
%% API
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
-spec add_user(binary(), binary(), binary()) -> {ok, map()} | {error, any()}.
|
||||
add_user(Username, Password, Desc) when
|
||||
-spec add_user(binary(), binary(), dashboard_user_role(), binary()) -> {ok, map()} | {error, any()}.
|
||||
add_user(Username, Password, Role, Desc) when
|
||||
is_binary(Username), is_binary(Password)
|
||||
->
|
||||
case {legal_username(Username), legal_password(Password)} of
|
||||
{ok, ok} -> do_add_user(Username, Password, Desc);
|
||||
{{error, Reason}, _} -> {error, Reason};
|
||||
{_, {error, Reason}} -> {error, Reason}
|
||||
case {legal_username(Username), legal_password(Password), legal_role(Role)} of
|
||||
{ok, ok, ok} -> do_add_user(Username, Password, Role, Desc);
|
||||
{{error, Reason}, _, _} -> {error, Reason};
|
||||
{_, {error, Reason}, _} -> {error, Reason};
|
||||
{_, _, {error, Reason}} -> {error, Reason}
|
||||
end.
|
||||
|
||||
do_add_user(Username, Password, Desc) ->
|
||||
Res = mria:transaction(?DASHBOARD_SHARD, fun add_user_/3, [Username, Password, Desc]),
|
||||
do_add_user(Username, Password, Role, Desc) ->
|
||||
Res = mria:transaction(?DASHBOARD_SHARD, fun add_user_/4, [Username, Password, Role, Desc]),
|
||||
return(Res).
|
||||
|
||||
%% 0-9 or A-Z or a-z or $_
|
||||
|
@ -177,11 +178,12 @@ ascii_character_validate(Password) ->
|
|||
contain(Xs, Spec) -> lists:any(fun(X) -> lists:member(X, Spec) end, Xs).
|
||||
|
||||
%% black-magic: force overwrite a user
|
||||
force_add_user(Username, Password, Desc) ->
|
||||
force_add_user(Username, Password, Role, Desc) ->
|
||||
AddFun = fun() ->
|
||||
mnesia:write(#?ADMIN{
|
||||
username = Username,
|
||||
pwdhash = hash(Password),
|
||||
role = Role,
|
||||
description = Desc
|
||||
})
|
||||
end,
|
||||
|
@ -191,12 +193,12 @@ force_add_user(Username, Password, Desc) ->
|
|||
end.
|
||||
|
||||
%% @private
|
||||
add_user_(Username, Password, Desc) ->
|
||||
add_user_(Username, Password, Role, Desc) ->
|
||||
case mnesia:wread({?ADMIN, Username}) of
|
||||
[] ->
|
||||
Admin = #?ADMIN{username = Username, pwdhash = hash(Password), description = Desc},
|
||||
mnesia:write(Admin),
|
||||
#{username => Username, description => Desc};
|
||||
#{username => Username, role => Role, description => Desc};
|
||||
[_] ->
|
||||
mnesia:abort(<<"username_already_exist">>)
|
||||
end.
|
||||
|
@ -217,9 +219,14 @@ remove_user(Username) when is_binary(Username) ->
|
|||
{error, Reason}
|
||||
end.
|
||||
|
||||
-spec update_user(binary(), binary()) -> {ok, map()} | {error, term()}.
|
||||
update_user(Username, Desc) when is_binary(Username) ->
|
||||
return(mria:transaction(?DASHBOARD_SHARD, fun update_user_/2, [Username, Desc])).
|
||||
-spec update_user(binary(), dashboard_user_role(), binary()) -> {ok, map()} | {error, term()}.
|
||||
update_user(Username, Role, Desc) when is_binary(Username) ->
|
||||
case legal_role(Role) of
|
||||
ok ->
|
||||
return(mria:transaction(?DASHBOARD_SHARD, fun update_user_/3, [Username, Role, Desc]));
|
||||
Error ->
|
||||
Error
|
||||
end.
|
||||
|
||||
hash(Password) ->
|
||||
SaltBin = emqx_dashboard_token:salt(),
|
||||
|
@ -240,18 +247,18 @@ sha256(SaltBin, Password) ->
|
|||
crypto:hash('sha256', <<SaltBin/binary, Password/binary>>).
|
||||
|
||||
%% @private
|
||||
update_user_(Username, Desc) ->
|
||||
update_user_(Username, Role, Desc) ->
|
||||
case mnesia:wread({?ADMIN, Username}) of
|
||||
[] ->
|
||||
mnesia:abort(<<"username_not_found">>);
|
||||
[Admin] ->
|
||||
mnesia:write(Admin#?ADMIN{description = Desc}),
|
||||
#{username => Username, description => Desc}
|
||||
mnesia:write(Admin#?ADMIN{role = Role, description = Desc}),
|
||||
#{username => Username, role => Role, description => Desc}
|
||||
end.
|
||||
|
||||
change_password(Username, OldPasswd, NewPasswd) when is_binary(Username) ->
|
||||
case check(Username, OldPasswd) of
|
||||
ok -> change_password(Username, NewPasswd);
|
||||
{ok, _} -> change_password(Username, NewPasswd);
|
||||
Error -> Error
|
||||
end.
|
||||
|
||||
|
@ -320,9 +327,9 @@ check(_, undefined) ->
|
|||
{error, <<"password_not_provided">>};
|
||||
check(Username, Password) ->
|
||||
case lookup_user(Username) of
|
||||
[#?ADMIN{pwdhash = PwdHash}] ->
|
||||
[#?ADMIN{pwdhash = PwdHash} = User] ->
|
||||
case verify_hash(Password, PwdHash) of
|
||||
ok -> ok;
|
||||
ok -> {ok, User};
|
||||
error -> {error, <<"password_error">>}
|
||||
end;
|
||||
[] ->
|
||||
|
@ -333,14 +340,14 @@ check(Username, Password) ->
|
|||
%% token
|
||||
sign_token(Username, Password) ->
|
||||
case check(Username, Password) of
|
||||
ok ->
|
||||
emqx_dashboard_token:sign(Username, Password);
|
||||
{ok, User} ->
|
||||
emqx_dashboard_token:sign(User, Password);
|
||||
Error ->
|
||||
Error
|
||||
end.
|
||||
|
||||
verify_token(Token) ->
|
||||
emqx_dashboard_token:verify(Token).
|
||||
verify_token(Req, Token) ->
|
||||
emqx_dashboard_token:verify(Req, Token).
|
||||
|
||||
destroy_token_by_username(Username, Token) ->
|
||||
case emqx_dashboard_token:lookup(Token) of
|
||||
|
@ -363,10 +370,21 @@ add_default_user(Username, Password) when ?EMPTY_KEY(Username) orelse ?EMPTY_KEY
|
|||
{ok, empty};
|
||||
add_default_user(Username, Password) ->
|
||||
case lookup_user(Username) of
|
||||
[] -> do_add_user(Username, Password, <<"administrator">>);
|
||||
[] -> do_add_user(Username, Password, ?ROLE_SUPERUSER, <<"administrator">>);
|
||||
_ -> {ok, default_user_exists}
|
||||
end.
|
||||
|
||||
-if(?EMQX_RELEASE_EDITION == ee).
|
||||
legal_role(Role) ->
|
||||
emqx_dashboard_rbac:legal_role(Role).
|
||||
|
||||
-else.
|
||||
|
||||
legal_role(_) ->
|
||||
ok.
|
||||
|
||||
-endif.
|
||||
|
||||
-ifdef(TEST).
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
-behaviour(minirest_api).
|
||||
|
||||
-include("emqx_dashboard.hrl").
|
||||
-include_lib("hocon/include/hoconsc.hrl").
|
||||
-include_lib("emqx/include/logger.hrl").
|
||||
-include_lib("typerefl/include/types.hrl").
|
||||
|
@ -111,9 +112,9 @@ schema("/users") ->
|
|||
post => #{
|
||||
tags => [<<"dashboard">>],
|
||||
desc => ?DESC(create_user_api),
|
||||
'requestBody' => fields([username, password, description]),
|
||||
'requestBody' => fields([username, password, role, description]),
|
||||
responses => #{
|
||||
200 => fields([username, description])
|
||||
200 => fields([username, role, description])
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -124,9 +125,9 @@ schema("/users/:username") ->
|
|||
tags => [<<"dashboard">>],
|
||||
desc => ?DESC(update_user_api),
|
||||
parameters => fields([username_in_path]),
|
||||
'requestBody' => fields([description]),
|
||||
'requestBody' => fields([role, description]),
|
||||
responses => #{
|
||||
200 => fields([username, description]),
|
||||
200 => fields([username, role, description]),
|
||||
404 => response_schema(404)
|
||||
}
|
||||
},
|
||||
|
@ -170,7 +171,7 @@ response_schema(404) ->
|
|||
fields(user) ->
|
||||
fields([username, description]);
|
||||
fields(List) ->
|
||||
[field(Key) || Key <- List].
|
||||
[field(Key) || Key <- List, field_filter(Key)].
|
||||
|
||||
field(username) ->
|
||||
{username,
|
||||
|
@ -203,7 +204,9 @@ field(version) ->
|
|||
field(old_pwd) ->
|
||||
{old_pwd, mk(binary(), #{desc => ?DESC(old_pwd)})};
|
||||
field(new_pwd) ->
|
||||
{new_pwd, mk(binary(), #{desc => ?DESC(new_pwd)})}.
|
||||
{new_pwd, mk(binary(), #{desc => ?DESC(new_pwd)})};
|
||||
field(role) ->
|
||||
{role, mk(binary(), #{desc => ?DESC(role)})}.
|
||||
|
||||
%% -------------------------------------------------------------------------------------------------
|
||||
%% API
|
||||
|
@ -242,16 +245,17 @@ users(get, _Request) ->
|
|||
{200, emqx_dashboard_admin:all_users()};
|
||||
users(post, #{body := Params}) ->
|
||||
Desc = maps:get(<<"description">>, Params, <<"">>),
|
||||
Role = maps:get(<<"role">>, Params, ?ROLE_DEFAULT),
|
||||
Username = maps:get(<<"username">>, Params),
|
||||
Password = maps:get(<<"password">>, Params),
|
||||
case ?EMPTY(Username) orelse ?EMPTY(Password) of
|
||||
true ->
|
||||
{400, ?BAD_REQUEST, <<"Username or password undefined">>};
|
||||
false ->
|
||||
case emqx_dashboard_admin:add_user(Username, Password, Desc) of
|
||||
case emqx_dashboard_admin:add_user(Username, Password, Role, Desc) of
|
||||
{ok, Result} ->
|
||||
?SLOG(info, #{msg => "Create dashboard success", username => Username}),
|
||||
{200, Result};
|
||||
{200, filter_result(Result)};
|
||||
{error, Reason} ->
|
||||
?SLOG(info, #{
|
||||
msg => "Create dashboard failed",
|
||||
|
@ -263,12 +267,15 @@ users(post, #{body := Params}) ->
|
|||
end.
|
||||
|
||||
user(put, #{bindings := #{username := Username}, body := Params}) ->
|
||||
Role = maps:get(<<"role">>, Params, ?ROLE_DEFAULT),
|
||||
Desc = maps:get(<<"description">>, Params),
|
||||
case emqx_dashboard_admin:update_user(Username, Desc) of
|
||||
case emqx_dashboard_admin:update_user(Username, Role, Desc) of
|
||||
{ok, Result} ->
|
||||
{200, Result};
|
||||
{200, filter_result(Result)};
|
||||
{error, <<"username_not_found">> = Reason} ->
|
||||
{404, ?USER_NOT_FOUND, Reason};
|
||||
{error, Reason} ->
|
||||
{404, ?USER_NOT_FOUND, Reason}
|
||||
{400, ?BAD_REQUEST, Reason}
|
||||
end;
|
||||
user(delete, #{bindings := #{username := Username}, headers := Headers}) ->
|
||||
case Username == emqx_dashboard_admin:default_username() of
|
||||
|
@ -347,3 +354,22 @@ change_pwd(post, #{bindings := #{username := Username}, body := Params}) ->
|
|||
{400, ?BAD_REQUEST, Reason}
|
||||
end
|
||||
end.
|
||||
|
||||
-if(?EMQX_RELEASE_EDITION == ee).
|
||||
field_filter(_) ->
|
||||
true.
|
||||
|
||||
filter_result(Result) ->
|
||||
Result.
|
||||
|
||||
-else.
|
||||
|
||||
field_filter(role) ->
|
||||
false;
|
||||
field_filter(_) ->
|
||||
true.
|
||||
|
||||
filter_result(Result) ->
|
||||
maps:without([Role], Result).
|
||||
|
||||
-endif.
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
-module(emqx_dashboard_cli).
|
||||
|
||||
-include("emqx_dashboard.hrl").
|
||||
|
||||
-export([
|
||||
load/0,
|
||||
admins/1,
|
||||
|
@ -25,15 +27,6 @@
|
|||
load() ->
|
||||
emqx_ctl:register_command(admins, {?MODULE, admins}, []).
|
||||
|
||||
admins(["add", Username, Password]) ->
|
||||
admins(["add", Username, Password, ""]);
|
||||
admins(["add", Username, Password, Desc]) ->
|
||||
case emqx_dashboard_admin:add_user(bin(Username), bin(Password), bin(Desc)) of
|
||||
{ok, _} ->
|
||||
emqx_ctl:print("ok~n");
|
||||
{error, Reason} ->
|
||||
print_error(Reason)
|
||||
end;
|
||||
admins(["passwd", Username, Password]) ->
|
||||
case emqx_dashboard_admin:change_password(bin(Username), bin(Password)) of
|
||||
{ok, _} ->
|
||||
|
@ -48,14 +41,8 @@ admins(["del", Username]) ->
|
|||
{error, Reason} ->
|
||||
print_error(Reason)
|
||||
end;
|
||||
admins(_) ->
|
||||
emqx_ctl:usage(
|
||||
[
|
||||
{"admins add <Username> <Password> <Description>", "Add dashboard user"},
|
||||
{"admins passwd <Username> <Password>", "Reset dashboard user password"},
|
||||
{"admins del <Username>", "Delete dashboard user"}
|
||||
]
|
||||
).
|
||||
admins(Args) ->
|
||||
inner_admins(Args).
|
||||
|
||||
unload() ->
|
||||
emqx_ctl:unregister_command(admins).
|
||||
|
@ -67,3 +54,47 @@ print_error(Reason) when is_binary(Reason) ->
|
|||
%% Maybe has more types of error, but there is only binary now. So close it for dialyzer.
|
||||
% print_error(Reason) ->
|
||||
% emqx_ctl:print("Error: ~p~n", [Reason]).
|
||||
|
||||
-if(?EMQX_RELEASE_EDITION == ee).
|
||||
usage() ->
|
||||
[
|
||||
{"admins add <Username> <Password> <Role> <Description>", "Add dashboard user"},
|
||||
{"admins passwd <Username> <Password>", "Reset dashboard user password"},
|
||||
{"admins del <Username>", "Delete dashboard user"}
|
||||
].
|
||||
|
||||
inner_admins(["add", Username, Password]) ->
|
||||
inner_admins(["add", Username, Password, ?ROLE_SUPERUSER]);
|
||||
inner_admins(["add", Username, Password, Role]) ->
|
||||
inner_admins(["add", Username, Password, Role, ""]);
|
||||
inner_admins(["add", Username, Password, Role, Desc]) ->
|
||||
case emqx_dashboard_admin:add_user(bin(Username), bin(Password), bin(Role), bin(Desc)) of
|
||||
{ok, _} ->
|
||||
emqx_ctl:print("ok~n");
|
||||
{error, Reason} ->
|
||||
print_error(Reason)
|
||||
end;
|
||||
inner_admins(_) ->
|
||||
emqx_ctl:usage(usage()).
|
||||
-else.
|
||||
|
||||
usage() ->
|
||||
[
|
||||
{"admins add <Username> <Password> <Description>", "Add dashboard user"},
|
||||
{"admins passwd <Username> <Password>", "Reset dashboard user password"},
|
||||
{"admins del <Username>", "Delete dashboard user"}
|
||||
].
|
||||
|
||||
inner_admins(["add", Username, Password]) ->
|
||||
inner_admins(["add", Username, Password, ""]);
|
||||
inner_admins(["add", Username, Password, Desc]) ->
|
||||
case emqx_dashboard_admin:add_user(bin(Username), bin(Password), ?ROLE_SUPERUSER, bin(Desc)) of
|
||||
{ok, _} ->
|
||||
emqx_ctl:print("ok~n");
|
||||
{error, Reason} ->
|
||||
print_error(Reason)
|
||||
end;
|
||||
inner_admins(_) ->
|
||||
emqx_ctl:usage(usage()).
|
||||
|
||||
-endif.
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
-export([
|
||||
sign/2,
|
||||
verify/1,
|
||||
verify/2,
|
||||
lookup/1,
|
||||
owner/1,
|
||||
destroy/1,
|
||||
|
@ -55,14 +55,17 @@
|
|||
|
||||
%%--------------------------------------------------------------------
|
||||
%% jwt function
|
||||
-spec sign(Username :: binary(), Password :: binary()) ->
|
||||
-spec sign(User :: dashboard_user(), Password :: binary()) ->
|
||||
{ok, Token :: binary()} | {error, Reason :: term()}.
|
||||
sign(Username, Password) ->
|
||||
do_sign(Username, Password).
|
||||
sign(User, Password) ->
|
||||
do_sign(User, Password).
|
||||
|
||||
-spec verify(Token :: binary()) -> Result :: ok | {error, token_timeout | not_found}.
|
||||
verify(Token) ->
|
||||
do_verify(Token).
|
||||
-spec verify(_, Token :: binary()) ->
|
||||
Result ::
|
||||
ok
|
||||
| {error, token_timeout | not_found | unauthorized_role}.
|
||||
verify(Req, Token) ->
|
||||
do_verify(Req, Token).
|
||||
|
||||
-spec destroy(KeyOrKeys :: list() | binary() | #?ADMIN_JWT{}) -> ok.
|
||||
destroy([]) ->
|
||||
|
@ -101,7 +104,7 @@ mnesia(boot) ->
|
|||
|
||||
%%--------------------------------------------------------------------
|
||||
%% jwt apply
|
||||
do_sign(Username, Password) ->
|
||||
do_sign(#?ADMIN{username = Username, extra = Extra}, Password) ->
|
||||
ExpTime = jwt_expiration_time(),
|
||||
Salt = salt(),
|
||||
JWK = jwk(Username, Password, Salt),
|
||||
|
@ -114,22 +117,27 @@ do_sign(Username, Password) ->
|
|||
},
|
||||
Signed = jose_jwt:sign(JWK, JWS, JWT),
|
||||
{_, Token} = jose_jws:compact(Signed),
|
||||
JWTRec = format(Token, Username, ExpTime),
|
||||
JWTRec = format(Token, Username, ExpTime, Extra),
|
||||
_ = mria:transaction(?DASHBOARD_SHARD, fun mnesia:write/1, [JWTRec]),
|
||||
{ok, Token}.
|
||||
|
||||
do_verify(Token) ->
|
||||
do_verify(Req, Token) ->
|
||||
case lookup(Token) of
|
||||
{ok, JWT = #?ADMIN_JWT{exptime = ExpTime}} ->
|
||||
{ok, JWT = #?ADMIN_JWT{exptime = ExpTime, extra = Extra}} ->
|
||||
case ExpTime > erlang:system_time(millisecond) of
|
||||
true ->
|
||||
NewJWT = JWT#?ADMIN_JWT{exptime = jwt_expiration_time()},
|
||||
{atomic, Res} = mria:transaction(
|
||||
?DASHBOARD_SHARD,
|
||||
fun mnesia:write/1,
|
||||
[NewJWT]
|
||||
),
|
||||
Res;
|
||||
case check_rbac(Req, Extra) of
|
||||
true ->
|
||||
NewJWT = JWT#?ADMIN_JWT{exptime = jwt_expiration_time()},
|
||||
{atomic, Res} = mria:transaction(
|
||||
?DASHBOARD_SHARD,
|
||||
fun mnesia:write/1,
|
||||
[NewJWT]
|
||||
),
|
||||
Res;
|
||||
_ ->
|
||||
{error, unauthorized_role}
|
||||
end;
|
||||
_ ->
|
||||
{error, token_timeout}
|
||||
end;
|
||||
|
@ -183,11 +191,12 @@ jwt_expiration_time() ->
|
|||
token_ttl() ->
|
||||
emqx_conf:get([dashboard, token_expired_time], ?EXPTIME).
|
||||
|
||||
format(Token, Username, ExpTime) ->
|
||||
format(Token, Username, ExpTime, Extra) ->
|
||||
#?ADMIN_JWT{
|
||||
token = Token,
|
||||
username = Username,
|
||||
exptime = ExpTime
|
||||
exptime = ExpTime,
|
||||
extra = #{role => role(Extra)}
|
||||
}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
|
@ -234,3 +243,22 @@ clean_expired_jwt(Now) ->
|
|||
fun() -> mnesia:select(?TAB, Spec) end
|
||||
),
|
||||
ok = destroy(JWTList).
|
||||
|
||||
-if(?EMQX_RELEASE_EDITION == ee).
|
||||
check_rbac(Req, Extra) ->
|
||||
emqx_dashboard_rbac:check_rbac(Req, Extra).
|
||||
|
||||
role(Data) ->
|
||||
emqx_dashboard_rbac:role(Data).
|
||||
|
||||
-else.
|
||||
|
||||
-dialyzer({nowarn_function, [check_rbac/2]}).
|
||||
|
||||
check_rbac(_Req, _Extra) ->
|
||||
true.
|
||||
|
||||
role(_) ->
|
||||
undefined.
|
||||
|
||||
-endif.
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
src/emqx_ldap_filter_lexer.erl
|
||||
src/emqx_ldap_filter_parser.erl
|
|
@ -0,0 +1,94 @@
|
|||
Business Source License 1.1
|
||||
|
||||
Licensor: Hangzhou EMQ Technologies Co., Ltd.
|
||||
Licensed Work: EMQX Enterprise Edition
|
||||
The Licensed Work is (c) 2023
|
||||
Hangzhou EMQ Technologies Co., Ltd.
|
||||
Additional Use Grant: Students and educators are granted right to copy,
|
||||
modify, and create derivative work for research
|
||||
or education.
|
||||
Change Date: 2027-02-01
|
||||
Change License: Apache License, Version 2.0
|
||||
|
||||
For information about alternative licensing arrangements for the Software,
|
||||
please contact Licensor: https://www.emqx.com/en/contact
|
||||
|
||||
Notice
|
||||
|
||||
The Business Source License (this document, or the “License”) is not an Open
|
||||
Source license. However, the Licensed Work will eventually be made available
|
||||
under an Open Source License, as stated in this License.
|
||||
|
||||
License text copyright (c) 2017 MariaDB Corporation Ab, All Rights Reserved.
|
||||
“Business Source License” is a trademark of MariaDB Corporation Ab.
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
Business Source License 1.1
|
||||
|
||||
Terms
|
||||
|
||||
The Licensor hereby grants you the right to copy, modify, create derivative
|
||||
works, redistribute, and make non-production use of the Licensed Work. The
|
||||
Licensor may make an Additional Use Grant, above, permitting limited
|
||||
production use.
|
||||
|
||||
Effective on the Change Date, or the fourth anniversary of the first publicly
|
||||
available distribution of a specific version of the Licensed Work under this
|
||||
License, whichever comes first, the Licensor hereby grants you rights under
|
||||
the terms of the Change License, and the rights granted in the paragraph
|
||||
above terminate.
|
||||
|
||||
If your use of the Licensed Work does not comply with the requirements
|
||||
currently in effect as described in this License, you must purchase a
|
||||
commercial license from the Licensor, its affiliated entities, or authorized
|
||||
resellers, or you must refrain from using the Licensed Work.
|
||||
|
||||
All copies of the original and modified Licensed Work, and derivative works
|
||||
of the Licensed Work, are subject to this License. This License applies
|
||||
separately for each version of the Licensed Work and the Change Date may vary
|
||||
for each version of the Licensed Work released by Licensor.
|
||||
|
||||
You must conspicuously display this License on each original or modified copy
|
||||
of the Licensed Work. If you receive the Licensed Work in original or
|
||||
modified form from a third party, the terms and conditions set forth in this
|
||||
License apply to your use of that work.
|
||||
|
||||
Any use of the Licensed Work in violation of this License will automatically
|
||||
terminate your rights under this License for the current and all other
|
||||
versions of the Licensed Work.
|
||||
|
||||
This License does not grant you any right in any trademark or logo of
|
||||
Licensor or its affiliates (provided that you may use a trademark or logo of
|
||||
Licensor as expressly required by this License).
|
||||
|
||||
TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON
|
||||
AN “AS IS” BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS,
|
||||
EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND
|
||||
TITLE.
|
||||
|
||||
MariaDB hereby grants you permission to use this License’s text to license
|
||||
your works, and to refer to it using the trademark “Business Source License”,
|
||||
as long as you comply with the Covenants of Licensor below.
|
||||
|
||||
Covenants of Licensor
|
||||
|
||||
In consideration of the right to use this License’s text and the “Business
|
||||
Source License” name and trademark, Licensor covenants to MariaDB, and to all
|
||||
other recipients of the licensed work to be provided by Licensor:
|
||||
|
||||
1. To specify as the Change License the GPL Version 2.0 or any later version,
|
||||
or a license that is compatible with GPL Version 2.0 or a later version,
|
||||
where “compatible” means that software provided under the Change License can
|
||||
be included in a program with software provided under GPL Version 2.0 or a
|
||||
later version. Licensor may specify additional Change Licenses without
|
||||
limitation.
|
||||
|
||||
2. To either: (a) specify an additional grant of rights to use that does not
|
||||
impose any additional restriction on the right granted in this License, as
|
||||
the Additional Use Grant; or (b) insert the text “None”.
|
||||
|
||||
3. To specify a Change Date.
|
||||
|
||||
4. Not to modify this License in any other way.
|
|
@ -0,0 +1,15 @@
|
|||
# Dashboard Role-Based Access Control
|
||||
|
||||
RBAC (Role-Based Access Control) is a common access control model for managing user access to systems, applications or resources.
|
||||
In the RBAC model, access permissions are assigned and managed based on user roles instead of being directly associated with individual users,
|
||||
making management and usage simpler.
|
||||
|
||||
This application houses the RBAC feature for Dashboard.
|
||||
|
||||
## Contributing
|
||||
|
||||
Please see our [contributing.md](../../CONTRIBUTING.md).
|
||||
|
||||
## License
|
||||
|
||||
See [APL](../../APL.txt).
|
|
@ -0,0 +1 @@
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
%% -*- mode: erlang; -*-
|
||||
|
||||
{erl_opts, [debug_info]}.
|
||||
{deps, [
|
||||
{emqx_connector, {path, "../../apps/emqx_dashboard"}}
|
||||
]}.
|
|
@ -0,0 +1,14 @@
|
|||
{application, emqx_dashboard_rbac, [
|
||||
{description, "EMQX Dashboard RBAC"},
|
||||
{vsn, "0.1.0"},
|
||||
{registered, []},
|
||||
{applications, [
|
||||
kernel,
|
||||
stdlib,
|
||||
emqx_dashboard
|
||||
]},
|
||||
{env, []},
|
||||
{modules, []},
|
||||
|
||||
{links, []}
|
||||
]}.
|
|
@ -0,0 +1,44 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2023 EMQ Technologies Co., Ltd. All Rights Reserved.
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
-module(emqx_dashboard_rbac).
|
||||
|
||||
-include_lib("emqx_dashboard/include/emqx_dashboard.hrl").
|
||||
|
||||
-export([check_rbac/2, role/1, legal_role/1]).
|
||||
|
||||
-dialyzer({nowarn_function, role/1}).
|
||||
%%=====================================================================
|
||||
%% API
|
||||
check_rbac(Req, Extra) ->
|
||||
Method = cowboy_req:method(Req),
|
||||
Role = role(Extra),
|
||||
check_rbac_with_method(Role, Method).
|
||||
|
||||
role(#?ADMIN{role = undefined}) ->
|
||||
?ROLE_SUPERUSER;
|
||||
role(#?ADMIN{role = Role}) ->
|
||||
Role;
|
||||
role([]) ->
|
||||
?ROLE_SUPERUSER;
|
||||
role(#{role := Role}) ->
|
||||
Role.
|
||||
|
||||
legal_role(Role) ->
|
||||
case lists:member(Role, role_list()) of
|
||||
true ->
|
||||
ok;
|
||||
_ ->
|
||||
{error, <<"Role does not exist">>}
|
||||
end.
|
||||
%% ===================================================================
|
||||
check_rbac_with_method(?ROLE_SUPERUSER, _) ->
|
||||
true;
|
||||
check_rbac_with_method(?ROLE_VIEWER, <<"get">>) ->
|
||||
true;
|
||||
check_rbac_with_method(_, _) ->
|
||||
false.
|
||||
|
||||
role_list() ->
|
||||
[?ROLE_VIEWER, ?ROLE_SUPERUSER].
|
Loading…
Reference in New Issue