Merge pull request #8351 from HJianBo/compatible-password-field

Authn: backword compatibility for 4.x authn data
This commit is contained in:
JianBo He 2022-07-01 11:10:59 +08:00 committed by GitHub
commit 094ed05897
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 96 additions and 32 deletions

View File

@ -204,6 +204,9 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: make coveralls
- name: get coveralls logs
if: failure()
run: cat rebar3.crashdump
# do this in a separate job
upload_coverdata:

View File

@ -1,5 +1,11 @@
%% -*- mode: erlang -*-
%% Unless you know what you are doing, DO NOT edit manually!!
{VSN,
[{"0.1.0",[{load_module,emqx_authn_utils,brutal_purge,soft_purge,[]}]}],
[{"0.1.0",[{load_module,emqx_authn_utils,brutal_purge,soft_purge,[]}]}]}.
[{"0.1.0",[
{load_module,emqx_authn_utils,brutal_purge,soft_purge,[]},
{load_module,emqx_authn_redis,brutal_purge,soft_purge,[]}]
}],
[{"0.1.0",[
{load_module,emqx_authn_utils,brutal_purge,soft_purge,[]},
{load_module,emqx_authn_redis,brutal_purge,soft_purge,[]}]
}]}.

View File

@ -72,13 +72,25 @@ start_resource_if_enabled(Result, _ResourceId, _Config) ->
check_password_from_selected_map(_Algorithm, _Selected, undefined) ->
{error, bad_username_or_password};
check_password_from_selected_map(
Algorithm, #{<<"password_hash">> := Hash} = Selected, Password
) ->
Salt = maps:get(<<"salt">>, Selected, <<>>),
case emqx_authn_password_hashing:check_password(Algorithm, Salt, Hash, Password) of
true -> ok;
false -> {error, bad_username_or_password}
check_password_from_selected_map(Algorithm, Selected, Password) ->
Hash = maps:get(
<<"password_hash">>,
Selected,
maps:get(<<"password">>, Selected, undefined)
),
case Hash of
undefined ->
{error, not_authorized};
_ ->
Salt = maps:get(<<"salt">>, Selected, <<>>),
case
emqx_authn_password_hashing:check_password(
Algorithm, Salt, Hash, Password
)
of
true -> ok;
false -> {error, bad_username_or_password}
end
end.
parse_deep(Template) ->
@ -160,10 +172,14 @@ make_resource_id(Name) ->
handle_var({var, Name}, undefined) ->
error({cannot_get_variable, Name});
handle_var({var, <<"peerhost">>}, PeerHost) ->
emqx_placeholder:bin(inet:ntoa(PeerHost));
handle_var(_, Value) ->
emqx_placeholder:bin(Value).
handle_sql_var({var, Name}, undefined) ->
error({cannot_get_variable, Name});
handle_sql_var({var, <<"peerhost">>}, PeerHost) ->
emqx_placeholder:bin(inet:ntoa(PeerHost));
handle_sql_var(_, Value) ->
emqx_placeholder:sql_data(Value).

View File

@ -138,27 +138,16 @@ authenticate(
{ok, []} ->
ignore;
{ok, Values} ->
case merge(Fields, Values) of
#{<<"password_hash">> := _} = Selected ->
case
emqx_authn_utils:check_password_from_selected_map(
Algorithm, Selected, Password
)
of
ok ->
{ok, emqx_authn_utils:is_superuser(Selected)};
{error, Reason} ->
{error, Reason}
end;
_ ->
?SLOG(error, #{
msg => "cannot_find_password_hash_field",
cmd => Command,
keys => NKey,
fields => Fields,
resource => ResourceId
}),
ignore
Selected = merge(Fields, Values),
case
emqx_authn_utils:check_password_from_selected_map(
Algorithm, Selected, Password
)
of
ok ->
{ok, emqx_authn_utils:is_superuser(Selected)};
{error, Reason} ->
{error, Reason}
end;
{error, Reason} ->
?SLOG(error, #{
@ -210,8 +199,8 @@ parse_cmd(Cmd) ->
end.
check_fields(Fields) ->
HasPassHash = lists:member("password_hash", Fields),
KnownFields = ["password_hash", "salt", "is_superuser"],
HasPassHash = lists:member("password_hash", Fields) orelse lists:member("password", Fields),
KnownFields = ["password_hash", "password", "salt", "is_superuser"],
UnknownFields = [F || F <- Fields, not lists:member(F, KnownFields)],
case {HasPassHash, UnknownFields} of

View File

@ -29,8 +29,10 @@
-define(HTTP_PORT, 32333).
-define(HTTP_PATH, "/auth").
-define(CREDENTIALS, #{
clientid => <<"clienta">>,
username => <<"plain">>,
password => <<"plain">>,
peerhost => {127, 0, 0, 1},
listener => 'tcp:default',
protocol => mqtt
}).
@ -390,6 +392,32 @@ samples() ->
result => {ok, #{is_superuser => false}}
},
%% simple post request for placeholders, application/json
#{
handler => fun(Req0, State) ->
{ok, RawBody, Req1} = cowboy_req:read_body(Req0),
#{
<<"username">> := <<"plain">>,
<<"password">> := <<"plain">>,
<<"clientid">> := <<"clienta">>,
<<"peerhost">> := <<"127.0.0.1">>
} = jiffy:decode(RawBody, [return_maps]),
Req = cowboy_req:reply(200, Req1),
{ok, Req, State}
end,
config_params => #{
<<"method">> => <<"post">>,
<<"headers">> => #{<<"content-type">> => <<"application/json">>},
<<"body">> => #{
<<"clientid">> => ?PH_CLIENTID,
<<"username">> => ?PH_USERNAME,
<<"password">> => ?PH_PASSWORD,
<<"peerhost">> => ?PH_PEERHOST
}
},
result => {ok, #{is_superuser => false}}
},
%% custom headers
#{
handler => fun(Req0, State) ->

View File

@ -453,6 +453,28 @@ user_seeds() ->
<<"password_hash_algorithm">> => #{<<"name">> => <<"bcrypt">>}
},
result => {error, bad_username_or_password}
},
#{
data => #{
password =>
<<"a3c7f6b085c3e5897ffb9b86f18a9d905063f8550a74444b5892e193c1b50428">>,
is_superuser => <<"1">>
},
credentials => #{
clientid => <<"sha256_no_salt">>,
password => <<"sha256_no_salt">>
},
key => <<"mqtt_user:sha256_no_salt">>,
config_params => #{
%% Needs to be compatible with emqx 4.x auth data
<<"cmd">> => <<"HMGET mqtt_user:${clientid} password is_superuser">>,
<<"password_hash_algorithm">> => #{
<<"name">> => <<"sha256">>,
<<"salt_position">> => <<"disable">>
}
},
result => {ok, #{is_superuser => true}}
}
].