chore: support strip double quote in authn/authz
more compatibility for https://github.com/emqx/emqx/pull/8827
This commit is contained in:
parent
53bc6f47e7
commit
a461375b30
|
@ -112,7 +112,8 @@ parse_sql(Template, ReplaceWith) ->
|
||||||
Template,
|
Template,
|
||||||
#{
|
#{
|
||||||
replace_with => ReplaceWith,
|
replace_with => ReplaceWith,
|
||||||
placeholders => ?AUTHN_PLACEHOLDERS
|
placeholders => ?AUTHN_PLACEHOLDERS,
|
||||||
|
strip_double_quote => true
|
||||||
}
|
}
|
||||||
).
|
).
|
||||||
|
|
||||||
|
|
|
@ -332,6 +332,32 @@ user_seeds() ->
|
||||||
result => {ok, #{is_superuser => true}}
|
result => {ok, #{is_superuser => true}}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
%% strip double quote support
|
||||||
|
#{
|
||||||
|
data => #{
|
||||||
|
username => "sha256",
|
||||||
|
password_hash => "ac63a624e7074776d677dd61a003b8c803eb11db004d0ec6ae032a5d7c9c5caf",
|
||||||
|
salt => "salt",
|
||||||
|
is_superuser_int => 1
|
||||||
|
},
|
||||||
|
credentials => #{
|
||||||
|
username => <<"sha256">>,
|
||||||
|
password => <<"sha256">>
|
||||||
|
},
|
||||||
|
config_params => #{
|
||||||
|
<<"query">> =>
|
||||||
|
<<
|
||||||
|
"SELECT password_hash, salt, is_superuser_int as is_superuser\n"
|
||||||
|
" FROM users where username = \"${username}\" LIMIT 1"
|
||||||
|
>>,
|
||||||
|
<<"password_hash_algorithm">> => #{
|
||||||
|
<<"name">> => <<"sha256">>,
|
||||||
|
<<"salt_position">> => <<"prefix">>
|
||||||
|
}
|
||||||
|
},
|
||||||
|
result => {ok, #{is_superuser => true}}
|
||||||
|
},
|
||||||
|
|
||||||
#{
|
#{
|
||||||
data => #{
|
data => #{
|
||||||
username => "sha256",
|
username => "sha256",
|
||||||
|
|
|
@ -394,6 +394,32 @@ user_seeds() ->
|
||||||
result => {ok, #{is_superuser => true}}
|
result => {ok, #{is_superuser => true}}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
%% strip double quote support
|
||||||
|
#{
|
||||||
|
data => #{
|
||||||
|
username => "sha256",
|
||||||
|
password_hash => "ac63a624e7074776d677dd61a003b8c803eb11db004d0ec6ae032a5d7c9c5caf",
|
||||||
|
salt => "salt",
|
||||||
|
is_superuser_int => 1
|
||||||
|
},
|
||||||
|
credentials => #{
|
||||||
|
username => <<"sha256">>,
|
||||||
|
password => <<"sha256">>
|
||||||
|
},
|
||||||
|
config_params => #{
|
||||||
|
<<"query">> =>
|
||||||
|
<<
|
||||||
|
"SELECT password_hash, salt, is_superuser_int as is_superuser\n"
|
||||||
|
" FROM users where username = \"${username}\" LIMIT 1"
|
||||||
|
>>,
|
||||||
|
<<"password_hash_algorithm">> => #{
|
||||||
|
<<"name">> => <<"sha256">>,
|
||||||
|
<<"salt_position">> => <<"prefix">>
|
||||||
|
}
|
||||||
|
},
|
||||||
|
result => {ok, #{is_superuser => true}}
|
||||||
|
},
|
||||||
|
|
||||||
#{
|
#{
|
||||||
data => #{
|
data => #{
|
||||||
username => "sha256",
|
username => "sha256",
|
||||||
|
|
|
@ -110,7 +110,8 @@ parse_sql(Template, ReplaceWith, PlaceHolders) ->
|
||||||
Template,
|
Template,
|
||||||
#{
|
#{
|
||||||
replace_with => ReplaceWith,
|
replace_with => ReplaceWith,
|
||||||
placeholders => PlaceHolders
|
placeholders => PlaceHolders,
|
||||||
|
strip_double_quote => true
|
||||||
}
|
}
|
||||||
).
|
).
|
||||||
|
|
||||||
|
|
|
@ -202,6 +202,34 @@ t_lookups(_Config) ->
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
|
|
||||||
|
ok = emqx_authz_test_lib:test_samples(
|
||||||
|
ClientInfo,
|
||||||
|
[
|
||||||
|
{allow, subscribe, <<"a">>},
|
||||||
|
{deny, subscribe, <<"b">>}
|
||||||
|
]
|
||||||
|
),
|
||||||
|
|
||||||
|
%% strip double quote support
|
||||||
|
|
||||||
|
ok = init_table(),
|
||||||
|
ok = q(
|
||||||
|
<<
|
||||||
|
"INSERT INTO acl(clientid, topic, permission, action)"
|
||||||
|
"VALUES(?, ?, ?, ?)"
|
||||||
|
>>,
|
||||||
|
[<<"clientid">>, <<"a">>, <<"allow">>, <<"subscribe">>]
|
||||||
|
),
|
||||||
|
|
||||||
|
ok = setup_config(
|
||||||
|
#{
|
||||||
|
<<"query">> => <<
|
||||||
|
"SELECT permission, action, topic "
|
||||||
|
"FROM acl WHERE clientid = \"${clientid}\""
|
||||||
|
>>
|
||||||
|
}
|
||||||
|
),
|
||||||
|
|
||||||
ok = emqx_authz_test_lib:test_samples(
|
ok = emqx_authz_test_lib:test_samples(
|
||||||
ClientInfo,
|
ClientInfo,
|
||||||
[
|
[
|
||||||
|
|
|
@ -202,6 +202,34 @@ t_lookups(_Config) ->
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
|
|
||||||
|
ok = emqx_authz_test_lib:test_samples(
|
||||||
|
ClientInfo,
|
||||||
|
[
|
||||||
|
{allow, subscribe, <<"a">>},
|
||||||
|
{deny, subscribe, <<"b">>}
|
||||||
|
]
|
||||||
|
),
|
||||||
|
|
||||||
|
%% strip double quote support
|
||||||
|
|
||||||
|
ok = init_table(),
|
||||||
|
ok = insert(
|
||||||
|
<<
|
||||||
|
"INSERT INTO acl(clientid, topic, permission, action)"
|
||||||
|
"VALUES($1, $2, $3, $4)"
|
||||||
|
>>,
|
||||||
|
[<<"clientid">>, <<"a">>, <<"allow">>, <<"subscribe">>]
|
||||||
|
),
|
||||||
|
|
||||||
|
ok = setup_config(
|
||||||
|
#{
|
||||||
|
<<"query">> => <<
|
||||||
|
"SELECT permission, action, topic "
|
||||||
|
"FROM acl WHERE clientid = \"${clientid}\""
|
||||||
|
>>
|
||||||
|
}
|
||||||
|
),
|
||||||
|
|
||||||
ok = emqx_authz_test_lib:test_samples(
|
ok = emqx_authz_test_lib:test_samples(
|
||||||
ClientInfo,
|
ClientInfo,
|
||||||
[
|
[
|
||||||
|
|
|
@ -39,7 +39,10 @@
|
||||||
sql_data/1
|
sql_data/1
|
||||||
]).
|
]).
|
||||||
|
|
||||||
-define(EX_PLACE_HOLDER, "(\\$\\{[a-zA-Z0-9\\._]+\\}|\"\\$\\{[a-zA-Z0-9\\._]+\\}\")").
|
-define(EX_PLACE_HOLDER, "(\\$\\{[a-zA-Z0-9\\._]+\\})").
|
||||||
|
|
||||||
|
-define(EX_PLACE_HOLDER_DOUBLE_QUOTE, "(\\$\\{[a-zA-Z0-9\\._]+\\}|\"\\$\\{[a-zA-Z0-9\\._]+\\}\")").
|
||||||
|
|
||||||
%% Space and CRLF
|
%% Space and CRLF
|
||||||
-define(EX_WITHE_CHARS, "\\s").
|
-define(EX_WITHE_CHARS, "\\s").
|
||||||
|
|
||||||
|
@ -57,7 +60,8 @@
|
||||||
|
|
||||||
-type preproc_sql_opts() :: #{
|
-type preproc_sql_opts() :: #{
|
||||||
placeholders => list(binary()),
|
placeholders => list(binary()),
|
||||||
replace_with => '?' | '$n'
|
replace_with => '?' | '$n',
|
||||||
|
strip_double_quote => boolean()
|
||||||
}.
|
}.
|
||||||
|
|
||||||
-type preproc_deep_opts() :: #{
|
-type preproc_deep_opts() :: #{
|
||||||
|
@ -89,7 +93,7 @@ preproc_tmpl(Str) ->
|
||||||
preproc_tmpl(Str, Opts) ->
|
preproc_tmpl(Str, Opts) ->
|
||||||
RE = preproc_var_re(Opts),
|
RE = preproc_var_re(Opts),
|
||||||
Tokens = re:split(Str, RE, [{return, binary}, group, trim]),
|
Tokens = re:split(Str, RE, [{return, binary}, group, trim]),
|
||||||
do_preproc_tmpl(Tokens, []).
|
do_preproc_tmpl(Opts, Tokens, []).
|
||||||
|
|
||||||
-spec proc_tmpl(tmpl_token(), map()) -> binary().
|
-spec proc_tmpl(tmpl_token(), map()) -> binary().
|
||||||
proc_tmpl(Tokens, Data) ->
|
proc_tmpl(Tokens, Data) ->
|
||||||
|
@ -140,10 +144,11 @@ preproc_sql(Sql, ReplaceWith) when is_atom(ReplaceWith) ->
|
||||||
preproc_sql(Sql, #{replace_with => ReplaceWith});
|
preproc_sql(Sql, #{replace_with => ReplaceWith});
|
||||||
preproc_sql(Sql, Opts) ->
|
preproc_sql(Sql, Opts) ->
|
||||||
RE = preproc_var_re(Opts),
|
RE = preproc_var_re(Opts),
|
||||||
|
Strip = maps:get(strip_double_quote, Opts, false),
|
||||||
ReplaceWith = maps:get(replace_with, Opts, '?'),
|
ReplaceWith = maps:get(replace_with, Opts, '?'),
|
||||||
case re:run(Sql, RE, [{capture, all_but_first, binary}, global]) of
|
case re:run(Sql, RE, [{capture, all_but_first, binary}, global]) of
|
||||||
{match, PlaceHolders} ->
|
{match, PlaceHolders} ->
|
||||||
PhKs = [parse_nested(unwrap(Phld)) || [Phld | _] <- PlaceHolders],
|
PhKs = [parse_nested(unwrap(Phld, Strip)) || [Phld | _] <- PlaceHolders],
|
||||||
{replace_with(Sql, RE, ReplaceWith), [{var, Phld} || Phld <- PhKs]};
|
{replace_with(Sql, RE, ReplaceWith), [{var, Phld} || Phld <- PhKs]};
|
||||||
nomatch ->
|
nomatch ->
|
||||||
{Sql, []}
|
{Sql, []}
|
||||||
|
@ -234,29 +239,36 @@ get_phld_var(Fun, Data) when is_function(Fun) ->
|
||||||
get_phld_var(Phld, Data) ->
|
get_phld_var(Phld, Data) ->
|
||||||
emqx_rule_maps:nested_get(Phld, Data).
|
emqx_rule_maps:nested_get(Phld, Data).
|
||||||
|
|
||||||
preproc_var_re(#{placeholders := PHs}) ->
|
preproc_var_re(#{placeholders := PHs, strip_double_quote := true}) ->
|
||||||
Res = [ph_to_re(PH) || PH <- PHs],
|
Res = [ph_to_re(PH) || PH <- PHs],
|
||||||
QuoteRes = ["\"" ++ Re ++ "\"" || Re <- Res],
|
QuoteRes = ["\"" ++ Re ++ "\"" || Re <- Res],
|
||||||
"(" ++ string:join(Res ++ QuoteRes, "|") ++ ")";
|
"(" ++ string:join(Res ++ QuoteRes, "|") ++ ")";
|
||||||
|
preproc_var_re(#{placeholders := PHs}) ->
|
||||||
|
"(" ++ string:join([ph_to_re(PH) || PH <- PHs], "|") ++ ")";
|
||||||
|
preproc_var_re(#{strip_double_quote := true}) ->
|
||||||
|
?EX_PLACE_HOLDER_DOUBLE_QUOTE;
|
||||||
preproc_var_re(#{}) ->
|
preproc_var_re(#{}) ->
|
||||||
?EX_PLACE_HOLDER.
|
?EX_PLACE_HOLDER.
|
||||||
|
|
||||||
ph_to_re(VarPH) ->
|
ph_to_re(VarPH) ->
|
||||||
re:replace(VarPH, "[\\$\\{\\}]", "\\\\&", [global, {return, list}]).
|
re:replace(VarPH, "[\\$\\{\\}]", "\\\\&", [global, {return, list}]).
|
||||||
|
|
||||||
do_preproc_tmpl([], Acc) ->
|
do_preproc_tmpl(_Opts, [], Acc) ->
|
||||||
lists:reverse(Acc);
|
lists:reverse(Acc);
|
||||||
do_preproc_tmpl([[Str, Phld] | Tokens], Acc) ->
|
do_preproc_tmpl(Opts, [[Str, Phld] | Tokens], Acc) ->
|
||||||
|
Strip = maps:get(strip_double_quote, Opts, false),
|
||||||
do_preproc_tmpl(
|
do_preproc_tmpl(
|
||||||
|
Opts,
|
||||||
Tokens,
|
Tokens,
|
||||||
put_head(
|
put_head(
|
||||||
var,
|
var,
|
||||||
parse_nested(unwrap(Phld)),
|
parse_nested(unwrap(Phld, Strip)),
|
||||||
put_head(str, Str, Acc)
|
put_head(str, Str, Acc)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
do_preproc_tmpl([[Str] | Tokens], Acc) ->
|
do_preproc_tmpl(Opts, [[Str] | Tokens], Acc) ->
|
||||||
do_preproc_tmpl(
|
do_preproc_tmpl(
|
||||||
|
Opts,
|
||||||
Tokens,
|
Tokens,
|
||||||
put_head(str, Str, Acc)
|
put_head(str, Str, Acc)
|
||||||
).
|
).
|
||||||
|
@ -293,10 +305,10 @@ parse_nested(Attr) ->
|
||||||
Nested -> {path, [{key, P} || P <- Nested]}
|
Nested -> {path, [{key, P} || P <- Nested]}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
unwrap(<<"${", Val/binary>>) ->
|
unwrap(<<"\"${", Val/binary>>, _StripDoubleQuote = true) ->
|
||||||
binary:part(Val, {0, byte_size(Val) - 1});
|
binary:part(Val, {0, byte_size(Val) - 2});
|
||||||
unwrap(<<"\"${", Val/binary>>) ->
|
unwrap(<<"${", Val/binary>>, _StripDoubleQuote) ->
|
||||||
binary:part(Val, {0, byte_size(Val) - 2}).
|
binary:part(Val, {0, byte_size(Val) - 1}).
|
||||||
|
|
||||||
quote_sql(Str) ->
|
quote_sql(Str) ->
|
||||||
quote(Str, <<"\\\\'">>).
|
quote(Str, <<"\\\\'">>).
|
||||||
|
|
|
@ -150,20 +150,25 @@ t_preproc_sql6(_) ->
|
||||||
emqx_placeholder:proc_sql(ParamsTokens, Selected)
|
emqx_placeholder:proc_sql(ParamsTokens, Selected)
|
||||||
).
|
).
|
||||||
|
|
||||||
t_preproc_sql7(_) ->
|
t_preproc_sql_strip_double_quote(_) ->
|
||||||
Selected = #{a => <<"a">>, b => <<"b">>},
|
Selected = #{a => <<"a">>, b => <<"b">>},
|
||||||
|
Opts = #{replace_with => '$n', placeholders => [<<"${a}">>]},
|
||||||
|
|
||||||
|
%% no strip_double_quote option: "${key}" -> "value"
|
||||||
{PrepareStatement, ParamsTokens} = emqx_placeholder:preproc_sql(
|
{PrepareStatement, ParamsTokens} = emqx_placeholder:preproc_sql(
|
||||||
<<"a:\"${a}\",b:\"${b}\"">>,
|
<<"a:\"${a}\",b:\"${b}\"">>,
|
||||||
#{
|
Opts
|
||||||
replace_with => '$n',
|
|
||||||
placeholders => [<<"${a}">>]
|
|
||||||
}
|
|
||||||
),
|
),
|
||||||
?assertEqual(<<"a:$1,b:\"${b}\"">>, PrepareStatement),
|
?assertEqual(<<"a:\"$1\",b:\"${b}\"">>, PrepareStatement),
|
||||||
?assertEqual(
|
?assertEqual([<<"a">>], emqx_placeholder:proc_sql(ParamsTokens, Selected)),
|
||||||
[<<"a">>],
|
|
||||||
emqx_placeholder:proc_sql(ParamsTokens, Selected)
|
%% strip_double_quote = true: "${key}" -> value
|
||||||
).
|
{PrepareStatement1, ParamsTokens1} = emqx_placeholder:preproc_sql(
|
||||||
|
<<"a:\"${a}\",b:\"${b}\"">>,
|
||||||
|
Opts#{strip_double_quote => true}
|
||||||
|
),
|
||||||
|
?assertEqual(<<"a:$1,b:\"${b}\"">>, PrepareStatement1),
|
||||||
|
?assertEqual([<<"a">>], emqx_placeholder:proc_sql(ParamsTokens1, Selected)).
|
||||||
|
|
||||||
t_preproc_tmpl_deep(_) ->
|
t_preproc_tmpl_deep(_) ->
|
||||||
Selected = #{a => <<"1">>, b => 1, c => 1.0, d => #{d1 => <<"hi">>}},
|
Selected = #{a => <<"1">>, b => 1, c => 1.0, d => #{d1 => <<"hi">>}},
|
||||||
|
|
Loading…
Reference in New Issue