Merge pull request #7888 from savonarola/fix-jwt-acl
fix(jwt auth): fix claim validation
This commit is contained in:
commit
bdfe4f89a1
|
@ -85,6 +85,8 @@ check_acl(ClientInfo = #{jwt_claims := Claims},
|
||||||
?DEBUG("acl_deny_due_to_invalid_jwt_exp", []),
|
?DEBUG("acl_deny_due_to_invalid_jwt_exp", []),
|
||||||
deny
|
deny
|
||||||
end;
|
end;
|
||||||
|
#{AclClaimName := Acl} ->
|
||||||
|
verify_acl(ClientInfo, Acl, PubSub, Topic);
|
||||||
_ ->
|
_ ->
|
||||||
?DEBUG("no_acl_jwt_claim", []),
|
?DEBUG("no_acl_jwt_claim", []),
|
||||||
ignore
|
ignore
|
||||||
|
|
|
@ -203,18 +203,32 @@ do_verify(JwsCompacted, [Jwk|More]) ->
|
||||||
|
|
||||||
check_claims(Claims) ->
|
check_claims(Claims) ->
|
||||||
Now = os:system_time(seconds),
|
Now = os:system_time(seconds),
|
||||||
Checker = [{<<"exp">>, fun(ExpireTime) ->
|
Checker = [{<<"exp">>, with_int_value(
|
||||||
Now < ExpireTime
|
fun(ExpireTime) -> Now < ExpireTime end)},
|
||||||
end},
|
{<<"iat">>, with_int_value(
|
||||||
{<<"iat">>, fun(IssueAt) ->
|
fun(IssueAt) -> IssueAt =< Now end)},
|
||||||
IssueAt =< Now
|
{<<"nbf">>, with_int_value(
|
||||||
end},
|
fun(NotBefore) -> NotBefore =< Now end)}
|
||||||
{<<"nbf">>, fun(NotBefore) ->
|
|
||||||
NotBefore =< Now
|
|
||||||
end}
|
|
||||||
],
|
],
|
||||||
do_check_claim(Checker, Claims).
|
do_check_claim(Checker, Claims).
|
||||||
|
|
||||||
|
with_int_value(Fun) ->
|
||||||
|
fun(Value) ->
|
||||||
|
case Value of
|
||||||
|
Int when is_integer(Int) -> Fun(Int);
|
||||||
|
Bin when is_binary(Bin) ->
|
||||||
|
case string:to_integer(Bin) of
|
||||||
|
{Int, <<>>} -> Fun(Int);
|
||||||
|
_ -> false
|
||||||
|
end;
|
||||||
|
Str when is_list(Str) ->
|
||||||
|
case string:to_integer(Str) of
|
||||||
|
{Int, ""} -> Fun(Int);
|
||||||
|
_ -> false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
|
||||||
do_check_claim([], Claims) ->
|
do_check_claim([], Claims) ->
|
||||||
Claims;
|
Claims;
|
||||||
do_check_claim([{K, F}|More], Claims) ->
|
do_check_claim([{K, F}|More], Claims) ->
|
||||||
|
|
|
@ -93,6 +93,52 @@ t_check_auth(_Config) ->
|
||||||
?assertEqual({error, invalid_signature}, Result2),
|
?assertEqual({error, invalid_signature}, Result2),
|
||||||
?assertMatch({error, _}, emqx_access_control:authenticate(Plain#{password => <<"asd">>})).
|
?assertMatch({error, _}, emqx_access_control:authenticate(Plain#{password => <<"asd">>})).
|
||||||
|
|
||||||
|
t_check_auth_invalid_exp(init, _Config) ->
|
||||||
|
application:unset_env(emqx_auth_jwt, verify_claims).
|
||||||
|
t_check_auth_invalid_exp(_Config) ->
|
||||||
|
Plain = #{clientid => <<"client1">>, username => <<"plain">>, zone => external},
|
||||||
|
Jwt0 = sign([{clientid, <<"client1">>},
|
||||||
|
{username, <<"plain">>},
|
||||||
|
{exp, [{foo, bar}]}], <<"HS256">>, <<"emqxsecret">>),
|
||||||
|
ct:pal("Jwt: ~p~n", [Jwt0]),
|
||||||
|
|
||||||
|
Result0 = emqx_access_control:authenticate(Plain#{password => Jwt0}),
|
||||||
|
ct:pal("Auth result: ~p~n", [Result0]),
|
||||||
|
?assertMatch({error, _}, Result0),
|
||||||
|
|
||||||
|
Jwt1 = sign([{clientid, <<"client1">>},
|
||||||
|
{username, <<"plain">>},
|
||||||
|
{exp, <<"foobar">>}], <<"HS256">>, <<"emqxsecret">>),
|
||||||
|
ct:pal("Jwt: ~p~n", [Jwt1]),
|
||||||
|
|
||||||
|
Result1 = emqx_access_control:authenticate(Plain#{password => Jwt1}),
|
||||||
|
ct:pal("Auth result: ~p~n", [Result1]),
|
||||||
|
?assertMatch({error, _}, Result1).
|
||||||
|
|
||||||
|
t_check_auth_str_exp(init, _Config) ->
|
||||||
|
application:unset_env(emqx_auth_jwt, verify_claims).
|
||||||
|
t_check_auth_str_exp(_Config) ->
|
||||||
|
Plain = #{clientid => <<"client1">>, username => <<"plain">>, zone => external},
|
||||||
|
Exp = integer_to_binary(os:system_time(seconds) + 3),
|
||||||
|
|
||||||
|
Jwt0 = sign([{clientid, <<"client1">>},
|
||||||
|
{username, <<"plain">>},
|
||||||
|
{exp, Exp}], <<"HS256">>, <<"emqxsecret">>),
|
||||||
|
ct:pal("Jwt: ~p~n", [Jwt0]),
|
||||||
|
|
||||||
|
Result0 = emqx_access_control:authenticate(Plain#{password => Jwt0}),
|
||||||
|
ct:pal("Auth result: ~p~n", [Result0]),
|
||||||
|
?assertMatch({ok, #{auth_result := success, jwt_claims := _}}, Result0),
|
||||||
|
|
||||||
|
Jwt1 = sign([{clientid, <<"client1">>},
|
||||||
|
{username, <<"plain">>},
|
||||||
|
{exp, <<"0">>}], <<"HS256">>, <<"emqxsecret">>),
|
||||||
|
ct:pal("Jwt: ~p~n", [Jwt1]),
|
||||||
|
|
||||||
|
Result1 = emqx_access_control:authenticate(Plain#{password => Jwt1}),
|
||||||
|
ct:pal("Auth result: ~p~n", [Result1]),
|
||||||
|
?assertMatch({error, _}, Result1).
|
||||||
|
|
||||||
t_check_claims(init, _Config) ->
|
t_check_claims(init, _Config) ->
|
||||||
application:set_env(emqx_auth_jwt, verify_claims, [{sub, <<"value">>}]).
|
application:set_env(emqx_auth_jwt, verify_claims, [{sub, <<"value">>}]).
|
||||||
t_check_claims(_Config) ->
|
t_check_claims(_Config) ->
|
||||||
|
@ -276,3 +322,26 @@ t_check_jwt_acl_expire(_Config) ->
|
||||||
emqtt:subscribe(C, <<"a/b">>, 0)),
|
emqtt:subscribe(C, <<"a/b">>, 0)),
|
||||||
|
|
||||||
ok = emqtt:disconnect(C).
|
ok = emqtt:disconnect(C).
|
||||||
|
|
||||||
|
t_check_jwt_acl_no_exp(init, _Config) ->
|
||||||
|
application:set_env(emqx_auth_jwt, verify_claims, [{sub, <<"value">>}]).
|
||||||
|
t_check_jwt_acl_no_exp(_Config) ->
|
||||||
|
Jwt = sign([{client_id, <<"client1">>},
|
||||||
|
{username, <<"plain">>},
|
||||||
|
{sub, value},
|
||||||
|
{acl, [{sub, [<<"a/b">>]}]}],
|
||||||
|
<<"HS256">>,
|
||||||
|
<<"emqxsecret">>),
|
||||||
|
|
||||||
|
{ok, C} = emqtt:start_link(
|
||||||
|
[{clean_start, true},
|
||||||
|
{proto_ver, v5},
|
||||||
|
{client_id, <<"client1">>},
|
||||||
|
{password, Jwt}]),
|
||||||
|
{ok, _} = emqtt:connect(C),
|
||||||
|
|
||||||
|
?assertMatch(
|
||||||
|
{ok, #{}, [0]},
|
||||||
|
emqtt:subscribe(C, <<"a/b">>, 0)),
|
||||||
|
|
||||||
|
ok = emqtt:disconnect(C).
|
||||||
|
|
Loading…
Reference in New Issue