fix: generate palce holder (#6250)
* fix: generate place holder * style: whitespace cleanup * refactor(authz): placeholder for athuz * test: authz test suite for placeholder * fix: lw place holder suite * fix: auth n redis suite Co-authored-by: JimMoen <LnJimMoen@outlook.com>
This commit is contained in:
parent
6fb464fc05
commit
21bd9bba55
|
@ -86,8 +86,8 @@ listeners.tcp.default {
|
|||
## Set to "" to disable the feature.
|
||||
##
|
||||
## Variables in mountpoint string:
|
||||
## - %c: clientid
|
||||
## - %u: username
|
||||
## - ${clientid}: clientid
|
||||
## - ${username}: username
|
||||
##
|
||||
## @doc listeners.tcp.<name>.mountpoint
|
||||
## ValueType: String
|
||||
|
@ -185,8 +185,8 @@ listeners.ssl.default {
|
|||
## Set to "" to disable the feature.
|
||||
##
|
||||
## Variables in mountpoint string:
|
||||
## - %c: clientid
|
||||
## - %u: username
|
||||
## - ${clientid}: clientid
|
||||
## - ${username}: username
|
||||
##
|
||||
## @doc listeners.ssl.<name>.mountpoint
|
||||
## ValueType: String
|
||||
|
@ -278,8 +278,8 @@ listeners.quic.default {
|
|||
## Set to "" to disable the feature.
|
||||
##
|
||||
## Variables in mountpoint string:
|
||||
## - %c: clientid
|
||||
## - %u: username
|
||||
## - ${clientid}: clientid
|
||||
## - ${username}: username
|
||||
##
|
||||
## @doc listeners.quic.<name>.mountpoint
|
||||
## ValueType: String
|
||||
|
@ -372,8 +372,8 @@ listeners.ws.default {
|
|||
## Set to "" to disable the feature.
|
||||
##
|
||||
## Variables in mountpoint string:
|
||||
## - %c: clientid
|
||||
## - %u: username
|
||||
## - ${clientid}: clientid
|
||||
## - ${username}: username
|
||||
##
|
||||
## @doc listeners.ws.<name>.mountpoint
|
||||
## ValueType: String
|
||||
|
@ -475,8 +475,8 @@ listeners.wss.default {
|
|||
## Set to "" to disable the feature.
|
||||
##
|
||||
## Variables in mountpoint string:
|
||||
## - %c: clientid
|
||||
## - %u: username
|
||||
## - ${clientid}: clientid
|
||||
## - ${username}: username
|
||||
##
|
||||
## @doc listeners.wss.<name>.mountpoint
|
||||
## ValueType: String
|
||||
|
|
|
@ -17,60 +17,96 @@
|
|||
-ifndef(EMQ_X_PLACEHOLDER_HRL).
|
||||
-define(EMQ_X_PLACEHOLDER_HRL, true).
|
||||
|
||||
-define(PH(Type), <<"${", Type/binary, "}">>).
|
||||
-define(PH(Type), <<"${", Type/binary, "}">> ).
|
||||
|
||||
%% action: publish/subscribe/all
|
||||
-define(PH_ACTION, ?PH(<<"action">>)).
|
||||
-define(PH_ACTION, <<"${action}">> ).
|
||||
|
||||
%% cert
|
||||
-define(PH_CRET_SUBJECT, ?PH(<<"cert_subject">>)).
|
||||
-define(PH_CRET_CN_NAME, ?PH(<<"cert_common_name">>)).
|
||||
-define(PH_CERT_SUBJECT, <<"${cert_subject}">> ).
|
||||
-define(PH_CERT_CN_NAME, <<"${cert_common_name}">> ).
|
||||
|
||||
%% MQTT
|
||||
-define(PH_PASSWORD, ?PH(<<"password">>)).
|
||||
-define(PH_CLIENTID, ?PH(<<"clientid">>)).
|
||||
-define(PH_FROM_CLIENTID, ?PH(<<"from_clienid">>)).
|
||||
-define(PH_USERNAME, ?PH(<<"username">>)).
|
||||
-define(PH_FROM_USERNAME, ?PH(<<"from_username">>)).
|
||||
-define(PH_TOPIC, ?PH(<<"topic">>)).
|
||||
-define(PH_PASSWORD, <<"${password}">> ).
|
||||
-define(PH_CLIENTID, <<"${clientid}">> ).
|
||||
-define(PH_FROM_CLIENTID, <<"${from_clientid}">> ).
|
||||
-define(PH_USERNAME, <<"${username}">> ).
|
||||
-define(PH_FROM_USERNAME, <<"${from_username}">> ).
|
||||
-define(PH_TOPIC, <<"${topic}">> ).
|
||||
%% MQTT payload
|
||||
-define(PH_PAYLOAD, ?PH(<<"payload">>)).
|
||||
-define(PH_PAYLOAD, <<"${payload}">> ).
|
||||
%% client IPAddress
|
||||
-define(PH_PEERHOST, ?PH(<<"peerhost">>)).
|
||||
-define(PH_PEERHOST, <<"${peerhost}">> ).
|
||||
%% ip & port
|
||||
-define(PH_HOST, <<"${host}">> ).
|
||||
-define(PH_PORT, <<"${port}">> ).
|
||||
%% Enumeration of message QoS 0,1,2
|
||||
-define(PH_QOS, ?PH(<<"qos">>)).
|
||||
-define(PH_FLAGS, ?PH(<<"flags">>)).
|
||||
-define(PH_QOS, <<"${qos}">> ).
|
||||
-define(PH_FLAGS, <<"${flags}">> ).
|
||||
%% Additional data related to process within the MQTT message
|
||||
-define(PH_HEADERS, ?PH(<<"hearders">>)).
|
||||
-define(PH_HEADERS, <<"${headers}">> ).
|
||||
%% protocol name
|
||||
-define(PH_PROTONAME, ?PH(<<"proto_name">>)).
|
||||
-define(PH_PROTONAME, <<"${proto_name}">> ).
|
||||
%% protocol version
|
||||
-define(PH_PROTOVER, ?PH(<<"proto_ver">>)).
|
||||
-define(PH_PROTOVER, <<"${proto_ver}">> ).
|
||||
%% MQTT keepalive interval
|
||||
-define(PH_KEEPALIVE, ?PH(<<"keepalive">>)).
|
||||
-define(PH_KEEPALIVE, <<"${keepalive}">> ).
|
||||
%% MQTT clean_start
|
||||
-define(PH_CLEAR_START, ?PH(<<"clean_start">>)).
|
||||
-define(PH_CLEAR_START, <<"${clean_start}">> ).
|
||||
%% MQTT Session Expiration time
|
||||
-define(PH_EXPIRY_INTERVAL, ?PH(<<"expiry_interval">>)).
|
||||
-define(PH_EXPIRY_INTERVAL, <<"${expiry_interval}">> ).
|
||||
|
||||
%% Time when PUBLISH message reaches Broker (ms)
|
||||
-define(PH_PUBLISH_RECEIVED_AT, ?PH(<<"publish_received_at">>)).
|
||||
-define(PH_PUBLISH_RECEIVED_AT, <<"${publish_received_at}">>).
|
||||
%% Mountpoint for bridging messages
|
||||
-define(PH_MOUNTPOINT, ?PH(<<"mountpoint">>)).
|
||||
-define(PH_MOUNTPOINT, <<"${mountpoint}">> ).
|
||||
%% IPAddress and Port of terminal
|
||||
-define(PH_PEERNAME, ?PH(<<"peername">>)).
|
||||
-define(PH_PEERNAME, <<"${peername}">> ).
|
||||
%% IPAddress and Port listened by emqx
|
||||
-define(PH_SOCKNAME, ?PH(<<"sockname">>)).
|
||||
-define(PH_SOCKNAME, <<"${sockname}">> ).
|
||||
%% whether it is MQTT bridge connection
|
||||
-define(PH_IS_BRIDGE, ?PH(<<"is_bridge">>)).
|
||||
-define(PH_IS_BRIDGE, <<"${is_bridge}">> ).
|
||||
%% Terminal connection completion time (s)
|
||||
-define(PH_CONNECTED_AT, ?PH(<<"connected_at">>)).
|
||||
-define(PH_CONNECTED_AT, <<"${connected_at}">> ).
|
||||
%% Event trigger time(millisecond)
|
||||
-define(PH_TIMESTAMP, ?PH(<<"timestamp">>)).
|
||||
-define(PH_TIMESTAMP, <<"${timestamp}">> ).
|
||||
%% Terminal disconnection completion time (s)
|
||||
-define(PH_DISCONNECTED_AT, ?PH(<<"disconnected_at">>)).
|
||||
-define(PH_DISCONNECTED_AT, <<"${disconnected_at}">> ).
|
||||
|
||||
-define(PH_NODE, ?PH(<<"node">>)).
|
||||
-define(PH_REASON, ?PH(<<"reason">>)).
|
||||
-define(PH_NODE, <<"${node}">> ).
|
||||
-define(PH_REASON, <<"${reason}">> ).
|
||||
|
||||
%% sync change these place holder with binary def.
|
||||
-define(PH_S_ACTION, "${action}" ).
|
||||
-define(PH_S_CERT_SUBJECT, "${cert_subject}" ).
|
||||
-define(PH_S_CERT_CN_NAME, "${cert_common_name}" ).
|
||||
-define(PH_S_PASSWORD, "${password}" ).
|
||||
-define(PH_S_CLIENTID, "${clientid}" ).
|
||||
-define(PH_S_FROM_CLIENTID, "${from_clientid}" ).
|
||||
-define(PH_S_USERNAME, "${username}" ).
|
||||
-define(PH_S_FROM_USERNAME, "${from_username}" ).
|
||||
-define(PH_S_TOPIC, "${topic}" ).
|
||||
-define(PH_S_PAYLOAD, "${payload}" ).
|
||||
-define(PH_S_PEERHOST, "${peerhost}" ).
|
||||
-define(PH_S_HOST, "${host}" ).
|
||||
-define(PH_S_PORT, "${port}" ).
|
||||
-define(PH_S_QOS, "${qos}" ).
|
||||
-define(PH_S_FLAGS, "${flags}" ).
|
||||
-define(PH_S_HEADERS, "${headers}" ).
|
||||
-define(PH_S_PROTONAME, "${proto_name}" ).
|
||||
-define(PH_S_PROTOVER, "${proto_ver}" ).
|
||||
-define(PH_S_KEEPALIVE, "${keepalive}" ).
|
||||
-define(PH_S_CLEAR_START, "${clean_start}" ).
|
||||
-define(PH_S_EXPIRY_INTERVAL, "${expiry_interval}" ).
|
||||
-define(PH_S_PUBLISH_RECEIVED_AT, "${publish_received_at}" ).
|
||||
-define(PH_S_MOUNTPOINT, "${mountpoint}" ).
|
||||
-define(PH_S_PEERNAME, "${peername}" ).
|
||||
-define(PH_S_SOCKNAME, "${sockname}" ).
|
||||
-define(PH_S_IS_BRIDGE, "${is_bridge}" ).
|
||||
-define(PH_S_CONNECTED_AT, "${connected_at}" ).
|
||||
-define(PH_S_TIMESTAMP, "${timestamp}" ).
|
||||
-define(PH_S_DISCONNECTED_AT, "${disconnected_at}" ).
|
||||
-define(PH_S_NODE, "${node}" ).
|
||||
-define(PH_S_REASON, "${reason}" ).
|
||||
|
||||
-endif.
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
-module(emqx_mountpoint).
|
||||
|
||||
-include("emqx.hrl").
|
||||
-include("emqx_placeholder.hrl").
|
||||
-include("types.hrl").
|
||||
|
||||
-export([ mount/2
|
||||
|
@ -68,12 +69,12 @@ replvar(undefined, _Vars) ->
|
|||
undefined;
|
||||
replvar(MountPoint, #{clientid := ClientId, username := Username}) ->
|
||||
lists:foldl(fun feed_var/2, MountPoint,
|
||||
[{<<"%c">>, ClientId}, {<<"%u">>, Username}]).
|
||||
[{?PH_CLIENTID, ClientId}, {?PH_USERNAME, Username}]).
|
||||
|
||||
feed_var({<<"%c">>, ClientId}, MountPoint) ->
|
||||
emqx_topic:feed_var(<<"%c">>, ClientId, MountPoint);
|
||||
feed_var({<<"%u">>, undefined}, MountPoint) ->
|
||||
feed_var({?PH_CLIENTID, ClientId}, MountPoint) ->
|
||||
emqx_topic:feed_var(?PH_CLIENTID, ClientId, MountPoint);
|
||||
feed_var({?PH_USERNAME, undefined}, MountPoint) ->
|
||||
MountPoint;
|
||||
feed_var({<<"%u">>, Username}, MountPoint) ->
|
||||
emqx_topic:feed_var(<<"%u">>, Username, MountPoint).
|
||||
feed_var({?PH_USERNAME, Username}, MountPoint) ->
|
||||
emqx_topic:feed_var(?PH_USERNAME, Username, MountPoint).
|
||||
|
||||
|
|
|
@ -55,12 +55,12 @@ t_unmount(_) ->
|
|||
t_replvar(_) ->
|
||||
?assertEqual(undefined, replvar(undefined, #{})),
|
||||
?assertEqual(<<"mount/user/clientid/">>,
|
||||
replvar(<<"mount/%u/%c/">>,
|
||||
replvar(<<"mount/${username}/${clientid}/">>,
|
||||
#{clientid => <<"clientid">>,
|
||||
username => <<"user">>
|
||||
})),
|
||||
?assertEqual(<<"mount/%u/clientid/">>,
|
||||
replvar(<<"mount/%u/%c/">>,
|
||||
?assertEqual(<<"mount/${username}/clientid/">>,
|
||||
replvar(<<"mount/${username}/${clientid}/">>,
|
||||
#{clientid => <<"clientid">>,
|
||||
username => undefined
|
||||
})).
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
-include_lib("typerefl/include/types.hrl").
|
||||
-include("emqx_authn.hrl").
|
||||
-include_lib("emqx/include/emqx_placeholder.hrl").
|
||||
|
||||
-import(hoconsc, [mk/2, ref/1]).
|
||||
-import(emqx_dashboard_swagger, [error_codes/2]).
|
||||
|
@ -969,8 +970,8 @@ authenticator_examples() ->
|
|||
<<"content-type">> => <<"application/json">>
|
||||
},
|
||||
body => #{
|
||||
<<"username">> => <<"${mqtt-username}">>,
|
||||
<<"password">> => <<"${mqtt-password}">>
|
||||
<<"username">> => ?PH_USERNAME,
|
||||
<<"password">> => ?PH_PASSWORD
|
||||
},
|
||||
pool_size => 8,
|
||||
connect_timeout => 5000,
|
||||
|
@ -988,7 +989,7 @@ authenticator_examples() ->
|
|||
secret => <<"mysecret">>,
|
||||
secret_base64_encoded => false,
|
||||
verify_claims => #{
|
||||
<<"username">> => <<"${mqtt-username}">>
|
||||
<<"username">> => ?PH_USERNAME
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -1001,7 +1002,7 @@ authenticator_examples() ->
|
|||
database => example,
|
||||
collection => users,
|
||||
selector => #{
|
||||
username => <<"${mqtt-username}">>
|
||||
username => ?PH_USERNAME
|
||||
},
|
||||
password_hash_field => <<"password_hash">>,
|
||||
salt_field => <<"salt">>,
|
||||
|
@ -1017,7 +1018,7 @@ authenticator_examples() ->
|
|||
backend => <<"redis">>,
|
||||
server => <<"127.0.0.1:6379">>,
|
||||
database => 0,
|
||||
query => <<"HMGET ${mqtt-username} password_hash salt">>,
|
||||
query => <<"HMGET ${username} password_hash salt">>,
|
||||
password_hash_algorithm => <<"sha256">>,
|
||||
salt_position => <<"prefix">>
|
||||
}
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
-module(emqx_authn_utils).
|
||||
|
||||
-include_lib("emqx/include/emqx_placeholder.hrl").
|
||||
|
||||
-export([ replace_placeholders/2
|
||||
, replace_placeholder/2
|
||||
, check_password/3
|
||||
|
@ -47,17 +49,17 @@ replace_placeholders([Placeholder | More], Credential, Acc) ->
|
|||
replace_placeholders(More, Credential, [convert_to_sql_param(V) | Acc])
|
||||
end.
|
||||
|
||||
replace_placeholder(<<"${mqtt-username}">>, Credential) ->
|
||||
replace_placeholder(?PH_USERNAME, Credential) ->
|
||||
maps:get(username, Credential, undefined);
|
||||
replace_placeholder(<<"${mqtt-clientid}">>, Credential) ->
|
||||
replace_placeholder(?PH_CLIENTID, Credential) ->
|
||||
maps:get(clientid, Credential, undefined);
|
||||
replace_placeholder(<<"${mqtt-password}">>, Credential) ->
|
||||
replace_placeholder(?PH_PASSWORD, Credential) ->
|
||||
maps:get(password, Credential, undefined);
|
||||
replace_placeholder(<<"${ip-address}">>, Credential) ->
|
||||
replace_placeholder(?PH_PEERHOST, Credential) ->
|
||||
maps:get(peerhost, Credential, undefined);
|
||||
replace_placeholder(<<"${cert-subject}">>, Credential) ->
|
||||
replace_placeholder(?PH_CERT_SUBJECT, Credential) ->
|
||||
maps:get(dn, Credential, undefined);
|
||||
replace_placeholder(<<"${cert-common-name}">>, Credential) ->
|
||||
replace_placeholder(?PH_CERT_CN_NAME, Credential) ->
|
||||
maps:get(cn, Credential, undefined);
|
||||
replace_placeholder(Constant, _) ->
|
||||
Constant.
|
||||
|
|
|
@ -82,10 +82,10 @@ handle_info({refresh_jwks, _TRef, refresh}, #{request_id := RequestID} = State)
|
|||
_ ->
|
||||
ok = httpc:cancel_request(RequestID),
|
||||
receive
|
||||
{http, _} -> ok
|
||||
after 0 ->
|
||||
ok
|
||||
end
|
||||
{http, _} -> ok
|
||||
after 0 ->
|
||||
ok
|
||||
end
|
||||
end,
|
||||
{noreply, refresh_jwks(State)};
|
||||
|
||||
|
|
|
@ -345,7 +345,7 @@ handle_placeholder(Placeholder0) ->
|
|||
Placeholder0
|
||||
end.
|
||||
|
||||
validate_placeholder(<<"mqtt-clientid">>) ->
|
||||
validate_placeholder(<<"clientid">>) ->
|
||||
clientid;
|
||||
validate_placeholder(<<"mqtt-username">>) ->
|
||||
validate_placeholder(<<"username">>) ->
|
||||
username.
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
"password_hash":"c5e46903df45e5dc096dc74657610dbee8deaacae656df88a1788f1847390242",
|
||||
"salt": "e378187547bf2d6f0545a3f441aa4d8a",
|
||||
"is_superuser": true
|
||||
|
||||
,
|
||||
{
|
||||
"user_id":"myuser2",
|
||||
|
|
|
@ -58,6 +58,10 @@ init_per_suite(Config) ->
|
|||
ok = emqx_common_test_helpers:start_apps(
|
||||
[emqx_authn, emqx_dashboard],
|
||||
fun set_special_configs/1),
|
||||
|
||||
?AUTHN:delete_chain(?GLOBAL),
|
||||
{ok, Chains} = ?AUTHN:list_chains(),
|
||||
?assertEqual(length(Chains), 0),
|
||||
Config.
|
||||
|
||||
end_per_suite(_Config) ->
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
-include("emqx_authn.hrl").
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
-include_lib("common_test/include/ct.hrl").
|
||||
-include_lib("emqx/include/emqx_placeholder.hrl").
|
||||
|
||||
all() ->
|
||||
emqx_common_test_helpers:all(?MODULE).
|
||||
|
@ -62,7 +63,9 @@ t_authn(_) ->
|
|||
<<"backend">> => <<"mysql">>,
|
||||
<<"server">> => <<"127.0.0.1:3306">>,
|
||||
<<"database">> => <<"mqtt">>,
|
||||
<<"query">> => <<"SELECT password_hash, salt FROM users where username = ${mqtt-username} LIMIT 1">>
|
||||
<<"query">> =>
|
||||
<<"SELECT password_hash, salt FROM users where username = ",
|
||||
?PH_USERNAME/binary, " LIMIT 1">>
|
||||
},
|
||||
{ok, _} = update_config([authentication], {create_authenticator, ?GLOBAL, Config}),
|
||||
|
||||
|
@ -77,14 +80,13 @@ t_authn(_) ->
|
|||
listener => 'tcp:default',
|
||||
protocol => mqtt,
|
||||
username => <<"good">>,
|
||||
password => Password},
|
||||
password => Password},
|
||||
?assertEqual({ok, #{is_superuser => false}}, emqx_access_control:authenticate(ClientInfo)),
|
||||
|
||||
ClientInfo2 = ClientInfo#{username => <<"bad">>},
|
||||
?assertEqual({error, not_authorized}, emqx_access_control:authenticate(ClientInfo2)),
|
||||
|
||||
emqx_authn_test_lib:delete_config(<<"password-based:mysql">>),
|
||||
?AUTHN:delete_chain(?GLOBAL).
|
||||
|
||||
update_config(Path, ConfigRequest) ->
|
||||
emqx:update_config(Path, ConfigRequest, #{rawconf_with_defaults => true}).
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
-include_lib("eunit/include/eunit.hrl").
|
||||
-include_lib("common_test/include/ct.hrl").
|
||||
-include_lib("epgsql/include/epgsql.hrl").
|
||||
-include_lib("emqx/include/emqx_placeholder.hrl").
|
||||
|
||||
all() ->
|
||||
emqx_common_test_helpers:all(?MODULE).
|
||||
|
@ -55,11 +56,12 @@ end_per_testcase(_, _Config) ->
|
|||
%%------------------------------------------------------------------------------
|
||||
|
||||
t_parse_query(_) ->
|
||||
Query1 = <<"${mqtt-username}">>,
|
||||
?assertEqual({<<"$1">>, [<<"${mqtt-username}">>]}, emqx_authn_pgsql:parse_query(Query1)),
|
||||
Query1 = ?PH_USERNAME,
|
||||
?assertEqual({<<"$1">>, [?PH_USERNAME]}, emqx_authn_pgsql:parse_query(Query1)),
|
||||
|
||||
Query2 = <<"${mqtt-username}, ${mqtt-clientid}">>,
|
||||
?assertEqual({<<"$1, $2">>, [<<"${mqtt-username}">>, <<"${mqtt-clientid}">>]}, emqx_authn_pgsql:parse_query(Query2)),
|
||||
Query2 = <<?PH_USERNAME/binary, ", ", ?PH_CLIENTID/binary>>,
|
||||
?assertEqual({<<"$1, $2">>, [?PH_USERNAME, ?PH_CLIENTID]},
|
||||
emqx_authn_pgsql:parse_query(Query2)),
|
||||
|
||||
Query3 = <<"nomatch">>,
|
||||
?assertEqual({<<"nomatch">>, []}, emqx_authn_pgsql:parse_query(Query3)).
|
||||
|
@ -73,13 +75,16 @@ t_authn(_) ->
|
|||
<<"backend">> => <<"postgresql">>,
|
||||
<<"server">> => <<"127.0.0.1:5432">>,
|
||||
<<"database">> => <<"mqtt">>,
|
||||
<<"query">> => <<"SELECT password_hash, salt FROM users where username = ${mqtt-username} LIMIT 1">>
|
||||
<<"query">> =>
|
||||
<<"SELECT password_hash, salt FROM users where username = ",
|
||||
?PH_USERNAME/binary, " LIMIT 1">>
|
||||
},
|
||||
{ok, _} = update_config([authentication], {create_authenticator, ?GLOBAL, Config}),
|
||||
|
||||
meck:expect(emqx_resource, query,
|
||||
fun(_, {sql, _, [<<"good">>]}) ->
|
||||
{ok, [#column{name = <<"password_hash">>}, #column{name = <<"salt">>}], [{PasswordHash, Salt}]};
|
||||
{ok, [#column{name = <<"password_hash">>}, #column{name = <<"salt">>}],
|
||||
[{PasswordHash, Salt}]};
|
||||
(_, {sql, _, _}) ->
|
||||
{error, this_is_a_fictitious_reason}
|
||||
end),
|
||||
|
@ -94,6 +99,7 @@ t_authn(_) ->
|
|||
ClientInfo2 = ClientInfo#{username => <<"bad">>},
|
||||
?assertEqual({error, not_authorized}, emqx_access_control:authenticate(ClientInfo2)),
|
||||
|
||||
emqx_authn_test_lib:delete_config(<<"password-based:postgresql">>),
|
||||
?AUTHN:delete_chain(?GLOBAL).
|
||||
|
||||
update_config(Path, ConfigRequest) ->
|
||||
|
|
|
@ -99,11 +99,11 @@ t_create_invalid(_Config) ->
|
|||
AuthConfig#{password => <<"wrongpass">>},
|
||||
AuthConfig#{database => <<"5678">>},
|
||||
AuthConfig#{
|
||||
query => <<"MGET password_hash:${mqtt-username} salt:${mqtt-username}">>},
|
||||
query => <<"MGET password_hash:${username} salt:${username}">>},
|
||||
AuthConfig#{
|
||||
query => <<"HMGET mqtt_user:${mqtt-username} password_hash invalid_field">>},
|
||||
query => <<"HMGET mqtt_user:${username} password_hash invalid_field">>},
|
||||
AuthConfig#{
|
||||
query => <<"HMGET mqtt_user:${mqtt-username} salt is_superuser">>}
|
||||
query => <<"HMGET mqtt_user:${username} salt is_superuser">>}
|
||||
],
|
||||
|
||||
lists:foreach(
|
||||
|
@ -178,7 +178,7 @@ t_update(_Config) ->
|
|||
CorrectConfig = raw_redis_auth_config(),
|
||||
IncorrectConfig =
|
||||
CorrectConfig#{
|
||||
query => <<"HMGET invalid_key:${mqtt-username} password_hash salt is_superuser">>},
|
||||
query => <<"HMGET invalid_key:${username} password_hash salt is_superuser">>},
|
||||
|
||||
{ok, _} = emqx:update_config(
|
||||
?PATH,
|
||||
|
@ -215,7 +215,7 @@ raw_redis_auth_config() ->
|
|||
enable => <<"true">>,
|
||||
|
||||
backend => <<"redis">>,
|
||||
query => <<"HMGET mqtt_user:${mqtt-username} password_hash salt is_superuser">>,
|
||||
query => <<"HMGET mqtt_user:${username} password_hash salt is_superuser">>,
|
||||
database => <<"1">>,
|
||||
password => <<"public">>,
|
||||
server => redis_server()
|
||||
|
@ -263,7 +263,7 @@ user_seeds() ->
|
|||
},
|
||||
key => "mqtt_user:sha256",
|
||||
config_params => #{
|
||||
query => <<"HMGET mqtt_user:${mqtt-clientid} password_hash salt is_superuser">>,
|
||||
query => <<"HMGET mqtt_user:${clientid} password_hash salt is_superuser">>,
|
||||
password_hash_algorithm => <<"sha256">>,
|
||||
salt_position => <<"prefix">>
|
||||
},
|
||||
|
@ -299,7 +299,7 @@ user_seeds() ->
|
|||
key => "mqtt_user:bcrypt0",
|
||||
config_params => #{
|
||||
% clientid variable & username credentials
|
||||
query => <<"HMGET mqtt_client:${mqtt-clientid} password_hash salt is_superuser">>,
|
||||
query => <<"HMGET mqtt_client:${clientid} password_hash salt is_superuser">>,
|
||||
password_hash_algorithm => <<"bcrypt">>,
|
||||
salt_position => <<"suffix">>
|
||||
},
|
||||
|
@ -318,7 +318,7 @@ user_seeds() ->
|
|||
key => "mqtt_user:bcrypt1",
|
||||
config_params => #{
|
||||
% Bad key in query
|
||||
query => <<"HMGET badkey:${mqtt-username} password_hash salt is_superuser">>,
|
||||
query => <<"HMGET badkey:${username} password_hash salt is_superuser">>,
|
||||
password_hash_algorithm => <<"bcrypt">>,
|
||||
salt_position => <<"suffix">>
|
||||
},
|
||||
|
@ -337,7 +337,7 @@ user_seeds() ->
|
|||
},
|
||||
key => "mqtt_user:bcrypt2",
|
||||
config_params => #{
|
||||
query => <<"HMGET mqtt_user:${mqtt-username} password_hash salt is_superuser">>,
|
||||
query => <<"HMGET mqtt_user:${username} password_hash salt is_superuser">>,
|
||||
password_hash_algorithm => <<"bcrypt">>,
|
||||
salt_position => <<"suffix">>
|
||||
},
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
-module(emqx_authn_test_lib).
|
||||
|
||||
-include("emqx_authn.hrl").
|
||||
|
||||
-compile(nowarn_export_all).
|
||||
-compile(export_all).
|
||||
|
||||
|
@ -45,3 +47,10 @@ delete_authenticators(Path, Chain) ->
|
|||
end,
|
||||
Authenticators)
|
||||
end.
|
||||
|
||||
delete_config(ID) ->
|
||||
{ok, _} =
|
||||
emqx:update_config(
|
||||
[authentication],
|
||||
{delete_authenticator, ?GLOBAL, ID},
|
||||
#{rawconf_with_defaults => false}).
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
-include("emqx_authz.hrl").
|
||||
-include_lib("emqx/include/emqx.hrl").
|
||||
-include_lib("emqx/include/logger.hrl").
|
||||
-include_lib("emqx/include/emqx_placeholder.hrl").
|
||||
|
||||
%% AuthZ Callbacks
|
||||
-export([ authorize/4
|
||||
|
@ -42,7 +43,7 @@ authorize(Client, PubSub, Topic,
|
|||
annotations := #{id := ResourceID}
|
||||
} = Source) ->
|
||||
Request = case Method of
|
||||
get ->
|
||||
get ->
|
||||
Query = maps:get(query, Url, ""),
|
||||
Path1 = replvar(Path ++ "?" ++ Query, PubSub, Topic, Client),
|
||||
{Path1, maps:to_list(Headers)};
|
||||
|
@ -68,7 +69,9 @@ query_string([], Acc) ->
|
|||
<<$&, Str/binary>> = iolist_to_binary(lists:reverse(Acc)),
|
||||
Str;
|
||||
query_string([{K, V} | More], Acc) ->
|
||||
query_string(More, [["&", emqx_http_lib:uri_encode(K), "=", emqx_http_lib:uri_encode(V)] | Acc]).
|
||||
query_string( More
|
||||
, [ ["&", emqx_http_lib:uri_encode(K), "=", emqx_http_lib:uri_encode(V)]
|
||||
| Acc]).
|
||||
|
||||
serialize_body(<<"application/json">>, Body) ->
|
||||
jsx:encode(Body);
|
||||
|
@ -84,13 +87,20 @@ replvar(Str0, PubSub, Topic,
|
|||
}) when is_list(Str0);
|
||||
is_binary(Str0) ->
|
||||
NTopic = emqx_http_lib:uri_encode(Topic),
|
||||
Str1 = re:replace(Str0, "%c", Clientid, [global, {return, binary}]),
|
||||
Str2 = re:replace(Str1, "%u", bin(Username), [global, {return, binary}]),
|
||||
Str3 = re:replace(Str2, "%a", inet_parse:ntoa(IpAddress), [global, {return, binary}]),
|
||||
Str4 = re:replace(Str3, "%r", bin(Protocol), [global, {return, binary}]),
|
||||
Str5 = re:replace(Str4, "%m", Mountpoint, [global, {return, binary}]),
|
||||
Str6 = re:replace(Str5, "%t", NTopic, [global, {return, binary}]),
|
||||
Str7 = re:replace(Str6, "%A", bin(PubSub), [global, {return, binary}]),
|
||||
Str1 = re:replace( Str0, ?PH_S_CLIENTID
|
||||
, Clientid, [global, {return, binary}]),
|
||||
Str2 = re:replace( Str1, ?PH_S_USERNAME
|
||||
, bin(Username), [global, {return, binary}]),
|
||||
Str3 = re:replace( Str2, ?PH_S_HOST
|
||||
, inet_parse:ntoa(IpAddress), [global, {return, binary}]),
|
||||
Str4 = re:replace( Str3, ?PH_S_PROTONAME
|
||||
, bin(Protocol), [global, {return, binary}]),
|
||||
Str5 = re:replace( Str4, ?PH_S_MOUNTPOINT
|
||||
, Mountpoint, [global, {return, binary}]),
|
||||
Str6 = re:replace( Str5, ?PH_S_TOPIC
|
||||
, NTopic, [global, {return, binary}]),
|
||||
Str7 = re:replace( Str6, ?PH_S_ACTION
|
||||
, bin(PubSub), [global, {return, binary}]),
|
||||
Str7.
|
||||
|
||||
bin(A) when is_atom(A) -> atom_to_binary(A, utf8);
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
-include("emqx_authz.hrl").
|
||||
-include_lib("emqx/include/emqx.hrl").
|
||||
-include_lib("emqx/include/logger.hrl").
|
||||
-include_lib("emqx/include/emqx_placeholder.hrl").
|
||||
|
||||
%% AuthZ Callbacks
|
||||
-export([ authorize/4
|
||||
|
@ -40,12 +41,16 @@ authorize(Client, PubSub, Topic,
|
|||
}) ->
|
||||
case emqx_resource:query(ResourceID, {find, Collection, replvar(Selector, Client), #{}}) of
|
||||
{error, Reason} ->
|
||||
?SLOG(error, #{msg => "query_mongo_error", reason => Reason, resource_id => ResourceID}),
|
||||
?SLOG(error, #{msg => "query_mongo_error",
|
||||
reason => Reason,
|
||||
resource_id => ResourceID}),
|
||||
nomatch;
|
||||
[] -> nomatch;
|
||||
Rows ->
|
||||
Rules = [ emqx_authz_rule:compile({Permission, all, Action, Topics})
|
||||
|| #{<<"topics">> := Topics, <<"permission">> := Permission, <<"action">> := Action} <- Rows],
|
||||
|| #{<<"topics">> := Topics,
|
||||
<<"permission">> := Permission,
|
||||
<<"action">> := Action} <- Rows],
|
||||
do_authorize(Client, PubSub, Topic, Rules)
|
||||
end.
|
||||
|
||||
|
@ -62,19 +67,23 @@ replvar(Selector, #{clientid := Clientid,
|
|||
peerhost := IpAddress
|
||||
}) ->
|
||||
Fun = fun
|
||||
_Fun(K, V, AccIn) when is_map(V) -> maps:put(K, maps:fold(_Fun, AccIn, V), AccIn);
|
||||
_Fun(K, V, AccIn) when is_list(V) ->
|
||||
InFun(K, V, AccIn) when is_map(V) ->
|
||||
maps:put(K, maps:fold(InFun, AccIn, V), AccIn);
|
||||
InFun(K, V, AccIn) when is_list(V) ->
|
||||
maps:put(K, [ begin
|
||||
[{K1, V1}] = maps:to_list(M),
|
||||
_Fun(K1, V1, AccIn)
|
||||
InFun(K1, V1, AccIn)
|
||||
end || M <- V],
|
||||
AccIn);
|
||||
_Fun(K, V, AccIn) when is_binary(V) ->
|
||||
V1 = re:replace(V, "%c", bin(Clientid), [global, {return, binary}]),
|
||||
V2 = re:replace(V1, "%u", bin(Username), [global, {return, binary}]),
|
||||
V3 = re:replace(V2, "%a", inet_parse:ntoa(IpAddress), [global, {return, binary}]),
|
||||
InFun(K, V, AccIn) when is_binary(V) ->
|
||||
V1 = re:replace( V, ?PH_S_CLIENTID
|
||||
, bin(Clientid), [global, {return, binary}]),
|
||||
V2 = re:replace( V1, ?PH_S_USERNAME
|
||||
, bin(Username), [global, {return, binary}]),
|
||||
V3 = re:replace( V2, ?PH_S_HOST
|
||||
, inet_parse:ntoa(IpAddress), [global, {return, binary}]),
|
||||
maps:put(K, V3, AccIn);
|
||||
_Fun(K, V, AccIn) -> maps:put(K, V, AccIn)
|
||||
InFun(K, V, AccIn) -> maps:put(K, V, AccIn)
|
||||
end,
|
||||
maps:fold(Fun, #{}, Selector).
|
||||
|
||||
|
@ -82,4 +91,3 @@ bin(A) when is_atom(A) -> atom_to_binary(A, utf8);
|
|||
bin(B) when is_binary(B) -> B;
|
||||
bin(L) when is_list(L) -> list_to_binary(L);
|
||||
bin(X) -> X.
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
-include("emqx_authz.hrl").
|
||||
-include_lib("emqx/include/emqx.hrl").
|
||||
-include_lib("emqx/include/logger.hrl").
|
||||
-include_lib("emqx/include/emqx_placeholder.hrl").
|
||||
|
||||
%% AuthZ Callbacks
|
||||
-export([ description/0
|
||||
|
@ -55,7 +56,9 @@ authorize(Client, PubSub, Topic,
|
|||
{ok, Columns, Rows} ->
|
||||
do_authorize(Client, PubSub, Topic, Columns, Rows);
|
||||
{error, Reason} ->
|
||||
?SLOG(error, #{msg => "query_mysql_error", reason => Reason, resource_id => ResourceID}),
|
||||
?SLOG(error, #{ msg => "query_mysql_error"
|
||||
, reason => Reason
|
||||
, resource_id => ResourceID}),
|
||||
nomatch
|
||||
end.
|
||||
|
||||
|
@ -87,16 +90,16 @@ replvar(Params, ClientInfo) ->
|
|||
replvar([], _ClientInfo, Acc) ->
|
||||
lists:reverse(Acc);
|
||||
|
||||
replvar(["'%u'" | Params], ClientInfo, Acc) ->
|
||||
replvar([?PH_S_USERNAME | Params], ClientInfo, Acc) ->
|
||||
replvar(Params, ClientInfo, [safe_get(username, ClientInfo) | Acc]);
|
||||
replvar(["'%c'" | Params], ClientInfo = #{clientid := ClientId}, Acc) ->
|
||||
replvar([?PH_S_CLIENTID | Params], ClientInfo = #{clientid := ClientId}, Acc) ->
|
||||
replvar(Params, ClientInfo, [ClientId | Acc]);
|
||||
replvar(["'%a'" | Params], ClientInfo = #{peerhost := IpAddr}, Acc) ->
|
||||
replvar([?PH_S_PEERHOST | Params], ClientInfo = #{peerhost := IpAddr}, Acc) ->
|
||||
replvar(Params, ClientInfo, [inet_parse:ntoa(IpAddr) | Acc]);
|
||||
replvar(["'%C'" | Params], ClientInfo, Acc) ->
|
||||
replvar(Params, ClientInfo, [safe_get(cn, ClientInfo)| Acc]);
|
||||
replvar(["'%d'" | Params], ClientInfo, Acc) ->
|
||||
replvar(Params, ClientInfo, [safe_get(dn, ClientInfo)| Acc]);
|
||||
replvar([?PH_S_CERT_CN_NAME | Params], ClientInfo, Acc) ->
|
||||
replvar(Params, ClientInfo, [safe_get(cn, ClientInfo) | Acc]);
|
||||
replvar([?PH_S_CERT_SUBJECT | Params], ClientInfo, Acc) ->
|
||||
replvar(Params, ClientInfo, [safe_get(dn, ClientInfo) | Acc]);
|
||||
replvar([Param | Params], ClientInfo, Acc) ->
|
||||
replvar(Params, ClientInfo, [Param | Acc]).
|
||||
|
||||
|
@ -107,4 +110,3 @@ bin(A) when is_atom(A) -> atom_to_binary(A, utf8);
|
|||
bin(B) when is_binary(B) -> B;
|
||||
bin(L) when is_list(L) -> list_to_binary(L);
|
||||
bin(X) -> X.
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
-include("emqx_authz.hrl").
|
||||
-include_lib("emqx/include/emqx.hrl").
|
||||
-include_lib("emqx/include/logger.hrl").
|
||||
-include_lib("emqx/include/emqx_placeholder.hrl").
|
||||
|
||||
%% AuthZ Callbacks
|
||||
-export([ description/0
|
||||
|
@ -59,7 +60,9 @@ authorize(Client, PubSub, Topic,
|
|||
{ok, Columns, Rows} ->
|
||||
do_authorize(Client, PubSub, Topic, Columns, Rows);
|
||||
{error, Reason} ->
|
||||
?SLOG(error, #{msg => "query_postgresql_error", reason => Reason, resource_id => ResourceID}),
|
||||
?SLOG(error, #{ msg => "query_postgresql_error"
|
||||
, reason => Reason
|
||||
, resource_id => ResourceID}),
|
||||
nomatch
|
||||
end.
|
||||
|
||||
|
@ -92,16 +95,16 @@ replvar(Params, ClientInfo) ->
|
|||
replvar([], _ClientInfo, Acc) ->
|
||||
lists:reverse(Acc);
|
||||
|
||||
replvar(["'%u'" | Params], ClientInfo, Acc) ->
|
||||
replvar([?PH_S_USERNAME | Params], ClientInfo, Acc) ->
|
||||
replvar(Params, ClientInfo, [safe_get(username, ClientInfo) | Acc]);
|
||||
replvar(["'%c'" | Params], ClientInfo = #{clientid := ClientId}, Acc) ->
|
||||
replvar([?PH_S_CLIENTID | Params], ClientInfo = #{clientid := ClientId}, Acc) ->
|
||||
replvar(Params, ClientInfo, [ClientId | Acc]);
|
||||
replvar(["'%a'" | Params], ClientInfo = #{peerhost := IpAddr}, Acc) ->
|
||||
replvar([?PH_S_PEERHOST | Params], ClientInfo = #{peerhost := IpAddr}, Acc) ->
|
||||
replvar(Params, ClientInfo, [inet_parse:ntoa(IpAddr) | Acc]);
|
||||
replvar(["'%C'" | Params], ClientInfo, Acc) ->
|
||||
replvar(Params, ClientInfo, [safe_get(cn, ClientInfo)| Acc]);
|
||||
replvar(["'%d'" | Params], ClientInfo, Acc) ->
|
||||
replvar(Params, ClientInfo, [safe_get(dn, ClientInfo)| Acc]);
|
||||
replvar([?PH_S_CERT_CN_NAME | Params], ClientInfo, Acc) ->
|
||||
replvar(Params, ClientInfo, [safe_get(cn, ClientInfo) | Acc]);
|
||||
replvar([?PH_S_CERT_SUBJECT | Params], ClientInfo, Acc) ->
|
||||
replvar(Params, ClientInfo, [safe_get(dn, ClientInfo) | Acc]);
|
||||
replvar([Param | Params], ClientInfo, Acc) ->
|
||||
replvar(Params, ClientInfo, [Param | Acc]).
|
||||
|
||||
|
@ -112,4 +115,3 @@ bin(A) when is_atom(A) -> atom_to_binary(A, utf8);
|
|||
bin(B) when is_binary(B) -> B;
|
||||
bin(L) when is_list(L) -> list_to_binary(L);
|
||||
bin(X) -> X.
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
-include("emqx_authz.hrl").
|
||||
-include_lib("emqx/include/emqx.hrl").
|
||||
-include_lib("emqx/include/logger.hrl").
|
||||
-include_lib("emqx/include/emqx_placeholder.hrl").
|
||||
|
||||
%% AuthZ Callbacks
|
||||
-export([ authorize/4
|
||||
|
@ -43,7 +44,9 @@ authorize(Client, PubSub, Topic,
|
|||
{ok, Rows} ->
|
||||
do_authorize(Client, PubSub, Topic, Rows);
|
||||
{error, Reason} ->
|
||||
?SLOG(error, #{msg => "query_redis_error", reason => Reason, resource_id => ResourceID}),
|
||||
?SLOG(error, #{ msg => "query_redis_error"
|
||||
, reason => Reason
|
||||
, resource_id => ResourceID}),
|
||||
nomatch
|
||||
end.
|
||||
|
||||
|
@ -58,13 +61,13 @@ do_authorize(Client, PubSub, Topic, [TopicFilter, Action | Tail]) ->
|
|||
end.
|
||||
|
||||
replvar(Cmd, Client = #{cn := CN}) ->
|
||||
replvar(repl(Cmd, "%C", CN), maps:remove(cn, Client));
|
||||
replvar(repl(Cmd, ?PH_S_CERT_CN_NAME, CN), maps:remove(cn, Client));
|
||||
replvar(Cmd, Client = #{dn := DN}) ->
|
||||
replvar(repl(Cmd, "%d", DN), maps:remove(dn, Client));
|
||||
replvar(repl(Cmd, ?PH_S_CERT_SUBJECT, DN), maps:remove(dn, Client));
|
||||
replvar(Cmd, Client = #{clientid := ClientId}) ->
|
||||
replvar(repl(Cmd, "%c", ClientId), maps:remove(clientid, Client));
|
||||
replvar(repl(Cmd, ?PH_S_CLIENTID, ClientId), maps:remove(clientid, Client));
|
||||
replvar(Cmd, Client = #{username := Username}) ->
|
||||
replvar(repl(Cmd, "%u", Username), maps:remove(username, Client));
|
||||
replvar(repl(Cmd, ?PH_S_USERNAME, Username), maps:remove(username, Client));
|
||||
replvar(Cmd, _) ->
|
||||
Cmd.
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
-include("emqx_authz.hrl").
|
||||
-include_lib("emqx/include/logger.hrl").
|
||||
-include_lib("emqx/include/emqx_placeholder.hrl").
|
||||
|
||||
-ifdef(TEST).
|
||||
-compile(export_all).
|
||||
|
@ -32,9 +33,12 @@
|
|||
|
||||
-export_type([rule/0]).
|
||||
|
||||
compile({Permission, all}) when ?ALLOW_DENY(Permission) -> {Permission, all, all, [compile_topic(<<"#">>)]};
|
||||
compile({Permission, Who, Action, TopicFilters}) when ?ALLOW_DENY(Permission), ?PUBSUB(Action), is_list(TopicFilters) ->
|
||||
{atom(Permission), compile_who(Who), atom(Action), [compile_topic(Topic) || Topic <- TopicFilters]}.
|
||||
compile({Permission, all})
|
||||
when ?ALLOW_DENY(Permission) -> {Permission, all, all, [compile_topic(<<"#">>)]};
|
||||
compile({Permission, Who, Action, TopicFilters})
|
||||
when ?ALLOW_DENY(Permission), ?PUBSUB(Action), is_list(TopicFilters) ->
|
||||
{ atom(Permission), compile_who(Who), atom(Action)
|
||||
, [compile_topic(Topic) || Topic <- TopicFilters]}.
|
||||
|
||||
compile_who(all) -> all;
|
||||
compile_who({user, Username}) -> compile_who({username, Username});
|
||||
|
@ -68,12 +72,12 @@ compile_topic(Topic) ->
|
|||
end.
|
||||
|
||||
pattern(Words) ->
|
||||
lists:member(<<"%u">>, Words) orelse lists:member(<<"%c">>, Words).
|
||||
lists:member(?PH_USERNAME, Words) orelse lists:member(?PH_CLIENTID, Words).
|
||||
|
||||
atom(B) when is_binary(B) ->
|
||||
try binary_to_existing_atom(B, utf8)
|
||||
catch
|
||||
_ -> binary_to_atom(B)
|
||||
_E:_S -> binary_to_atom(B)
|
||||
end;
|
||||
atom(A) when is_atom(A) -> A.
|
||||
|
||||
|
@ -143,11 +147,11 @@ match_who(_, _) -> false.
|
|||
|
||||
match_topics(_ClientInfo, _Topic, []) ->
|
||||
false;
|
||||
match_topics(ClientInfo, Topic, [{pattern, PatternFilter}|Filters]) ->
|
||||
match_topics(ClientInfo, Topic, [{pattern, PatternFilter} | Filters]) ->
|
||||
TopicFilter = feed_var(ClientInfo, PatternFilter),
|
||||
match_topic(emqx_topic:words(Topic), TopicFilter)
|
||||
orelse match_topics(ClientInfo, Topic, Filters);
|
||||
match_topics(ClientInfo, Topic, [TopicFilter|Filters]) ->
|
||||
match_topics(ClientInfo, Topic, [TopicFilter | Filters]) ->
|
||||
match_topic(emqx_topic:words(Topic), TopicFilter)
|
||||
orelse match_topics(ClientInfo, Topic, Filters).
|
||||
|
||||
|
@ -160,13 +164,13 @@ feed_var(ClientInfo, Pattern) ->
|
|||
feed_var(ClientInfo, Pattern, []).
|
||||
feed_var(_ClientInfo, [], Acc) ->
|
||||
lists:reverse(Acc);
|
||||
feed_var(ClientInfo = #{clientid := undefined}, [<<"%c">>|Words], Acc) ->
|
||||
feed_var(ClientInfo, Words, [<<"%c">>|Acc]);
|
||||
feed_var(ClientInfo = #{clientid := ClientId}, [<<"%c">>|Words], Acc) ->
|
||||
feed_var(ClientInfo, Words, [ClientId |Acc]);
|
||||
feed_var(ClientInfo = #{username := undefined}, [<<"%u">>|Words], Acc) ->
|
||||
feed_var(ClientInfo, Words, [<<"%u">>|Acc]);
|
||||
feed_var(ClientInfo = #{username := Username}, [<<"%u">>|Words], Acc) ->
|
||||
feed_var(ClientInfo, Words, [Username|Acc]);
|
||||
feed_var(ClientInfo, [W|Words], Acc) ->
|
||||
feed_var(ClientInfo, Words, [W|Acc]).
|
||||
feed_var(ClientInfo = #{clientid := undefined}, [?PH_CLIENTID | Words], Acc) ->
|
||||
feed_var(ClientInfo, Words, [?PH_CLIENTID | Acc]);
|
||||
feed_var(ClientInfo = #{clientid := ClientId}, [?PH_CLIENTID | Words], Acc) ->
|
||||
feed_var(ClientInfo, Words, [ClientId | Acc]);
|
||||
feed_var(ClientInfo = #{username := undefined}, [?PH_USERNAME | Words], Acc) ->
|
||||
feed_var(ClientInfo, Words, [?PH_USERNAME | Acc]);
|
||||
feed_var(ClientInfo = #{username := Username}, [?PH_USERNAME | Words], Acc) ->
|
||||
feed_var(ClientInfo, Words, [Username | Acc]);
|
||||
feed_var(ClientInfo, [W | Words], Acc) ->
|
||||
feed_var(ClientInfo, Words, [W | Acc]).
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
-include("emqx_authz.hrl").
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
-include_lib("common_test/include/ct.hrl").
|
||||
-include_lib("emqx/include/emqx_placeholder.hrl").
|
||||
|
||||
all() ->
|
||||
emqx_common_test_helpers:all(?MODULE).
|
||||
|
@ -107,7 +108,7 @@ set_special_configs(_App) ->
|
|||
<<"password">> => <<"ee">>,
|
||||
<<"auto_reconnect">> => true,
|
||||
<<"ssl">> => #{<<"enable">> => false},
|
||||
<<"cmd">> => <<"HGETALL mqtt_authz:%u">>
|
||||
<<"cmd">> => <<"HGETALL mqtt_authz:", ?PH_USERNAME/binary>>
|
||||
}).
|
||||
-define(SOURCE6, #{<<"type">> => <<"file">>,
|
||||
<<"enable">> => true,
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
-include("emqx_authz.hrl").
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
-include_lib("common_test/include/ct.hrl").
|
||||
-include_lib("emqx/include/emqx_placeholder.hrl").
|
||||
|
||||
-define(HOST, "http://127.0.0.1:18083/").
|
||||
-define(API_VERSION, "v5").
|
||||
|
@ -77,7 +78,7 @@
|
|||
<<"password">> => <<"ee">>,
|
||||
<<"auto_reconnect">> => true,
|
||||
<<"ssl">> => #{<<"enable">> => false},
|
||||
<<"cmd">> => <<"HGETALL mqtt_authz:%u">>
|
||||
<<"cmd">> => <<"HGETALL mqtt_authz:", ?PH_USERNAME/binary>>
|
||||
}).
|
||||
-define(SOURCE6, #{<<"type">> => <<"file">>,
|
||||
<<"enable">> => true,
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
-include("emqx_authz.hrl").
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
-include_lib("common_test/include/ct.hrl").
|
||||
-include_lib("emqx/include/emqx_placeholder.hrl").
|
||||
|
||||
all() ->
|
||||
emqx_common_test_helpers:all(?MODULE).
|
||||
|
@ -55,12 +56,12 @@ set_special_configs(_App) ->
|
|||
|
||||
init_per_testcase(t_authz, Config) ->
|
||||
mria:dirty_write(#emqx_acl{who = {?ACL_TABLE_USERNAME, <<"test_username">>},
|
||||
rules = [{allow, publish, <<"test/%u">>},
|
||||
rules = [{allow, publish, <<"test/", ?PH_S_USERNAME>>},
|
||||
{allow, subscribe, <<"eq #">>}
|
||||
]
|
||||
}),
|
||||
mria:dirty_write(#emqx_acl{who = {?ACL_TABLE_CLIENTID, <<"test_clientid">>},
|
||||
rules = [{allow, publish, <<"test/%c">>},
|
||||
rules = [{allow, publish, <<"test/", ?PH_S_CLIENTID>>},
|
||||
{deny, subscribe, <<"eq #">>}
|
||||
]
|
||||
}),
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
-include("emqx_authz.hrl").
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
-include_lib("common_test/include/ct.hrl").
|
||||
-include_lib("emqx/include/emqx_placeholder.hrl").
|
||||
|
||||
all() ->
|
||||
emqx_common_test_helpers:all(?MODULE).
|
||||
|
@ -74,10 +75,10 @@ set_special_configs(_App) ->
|
|||
-define(SOURCE2,[#{<<"topics">> => [<<"eq #">>],
|
||||
<<"permission">> => <<"allow">>,
|
||||
<<"action">> => <<"all">>}]).
|
||||
-define(SOURCE3,[#{<<"topics">> => [<<"test/%c">>],
|
||||
-define(SOURCE3,[#{<<"topics">> => [<<"test/", ?PH_CLIENTID/binary>>],
|
||||
<<"permission">> => <<"allow">>,
|
||||
<<"action">> => <<"subscribe">>}]).
|
||||
-define(SOURCE4,[#{<<"topics">> => [<<"test/%u">>],
|
||||
-define(SOURCE4,[#{<<"topics">> => [<<"test/", ?PH_USERNAME/binary>>],
|
||||
<<"permission">> => <<"allow">>,
|
||||
<<"action">> => <<"publish">>}]).
|
||||
|
||||
|
@ -131,4 +132,3 @@ t_authz(_) ->
|
|||
?assertEqual(deny, emqx_access_control:authorize(
|
||||
ClientInfo3, publish, <<"test">>)), % nomatch
|
||||
ok.
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
-include("emqx_authz.hrl").
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
-include_lib("common_test/include/ct.hrl").
|
||||
-include_lib("emqx/include/emqx_placeholder.hrl").
|
||||
|
||||
-define(CONF_DEFAULT, <<"authorization: {sources: []}">>).
|
||||
|
||||
|
@ -76,8 +77,8 @@ set_special_configs(_App) ->
|
|||
]).
|
||||
-define(SOURCE1, [[<<"all">>, <<"deny">>, <<"#">>]]).
|
||||
-define(SOURCE2, [[<<"all">>, <<"allow">>, <<"eq #">>]]).
|
||||
-define(SOURCE3, [[<<"subscribe">>, <<"allow">>, <<"test/%c">>]]).
|
||||
-define(SOURCE4, [[<<"publish">>, <<"allow">>, <<"test/%u">>]]).
|
||||
-define(SOURCE3, [[<<"subscribe">>, <<"allow">>, <<"test/", ?PH_CLIENTID/binary>>]]).
|
||||
-define(SOURCE4, [[<<"publish">>, <<"allow">>, <<"test/", ?PH_USERNAME/binary>>]]).
|
||||
|
||||
%%------------------------------------------------------------------------------
|
||||
%% Testcases
|
||||
|
@ -129,4 +130,3 @@ t_authz(_) ->
|
|||
?assertEqual(deny, emqx_access_control:authorize(
|
||||
ClientInfo3, publish, <<"test">>)), % nomatch
|
||||
ok.
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
-include("emqx_authz.hrl").
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
-include_lib("common_test/include/ct.hrl").
|
||||
-include_lib("emqx/include/emqx_placeholder.hrl").
|
||||
|
||||
all() ->
|
||||
emqx_common_test_helpers:all(?MODULE).
|
||||
|
@ -74,8 +75,8 @@ set_special_configs(_App) ->
|
|||
]).
|
||||
-define(SOURCE1, [{<<"all">>, <<"deny">>, <<"#">>}]).
|
||||
-define(SOURCE2, [{<<"all">>, <<"allow">>, <<"eq #">>}]).
|
||||
-define(SOURCE3, [{<<"subscribe">>, <<"allow">>, <<"test/%c">>}]).
|
||||
-define(SOURCE4, [{<<"publish">>, <<"allow">>, <<"test/%u">>}]).
|
||||
-define(SOURCE3, [{<<"subscribe">>, <<"allow">>, <<"test/", ?PH_CLIENTID/binary>>}]).
|
||||
-define(SOURCE4, [{<<"publish">>, <<"allow">>, <<"test/", ?PH_USERNAME/binary>>}]).
|
||||
|
||||
%%------------------------------------------------------------------------------
|
||||
%% Testcases
|
||||
|
@ -127,4 +128,3 @@ t_authz(_) ->
|
|||
?assertEqual(deny, emqx_access_control:authorize(
|
||||
ClientInfo3, publish, <<"test">>)), % nomatch
|
||||
ok.
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
-include("emqx_authz.hrl").
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
-include_lib("common_test/include/ct.hrl").
|
||||
-include_lib("emqx/include/emqx_placeholder.hrl").
|
||||
-define(CONF_DEFAULT, <<"authorization: {sources: []}">>).
|
||||
|
||||
all() ->
|
||||
|
@ -45,7 +46,7 @@ init_per_suite(Config) ->
|
|||
<<"password">> => <<"ee">>,
|
||||
<<"auto_reconnect">> => true,
|
||||
<<"ssl">> => #{<<"enable">> => false},
|
||||
<<"cmd">> => <<"HGETALL mqtt_authz:%u">>
|
||||
<<"cmd">> => <<"HGETALL mqtt_authz:", ?PH_USERNAME/binary>>
|
||||
}],
|
||||
{ok, _} = emqx_authz:update(replace, Rules),
|
||||
Config.
|
||||
|
@ -68,8 +69,8 @@ set_special_configs(emqx_authz) ->
|
|||
set_special_configs(_App) ->
|
||||
ok.
|
||||
|
||||
-define(SOURCE1, [<<"test/%u">>, <<"publish">>]).
|
||||
-define(SOURCE2, [<<"test/%c">>, <<"publish">>]).
|
||||
-define(SOURCE1, [<<"test/", ?PH_USERNAME/binary>>, <<"publish">>]).
|
||||
-define(SOURCE2, [<<"test/", ?PH_CLIENTID/binary>>, <<"publish">>]).
|
||||
-define(SOURCE3, [<<"#">>, <<"subscribe">>]).
|
||||
|
||||
%%------------------------------------------------------------------------------
|
||||
|
@ -113,4 +114,3 @@ t_authz(_) ->
|
|||
?assertEqual(deny,
|
||||
emqx_access_control:authorize(ClientInfo, publish, <<"#">>)),
|
||||
ok.
|
||||
|
||||
|
|
|
@ -21,15 +21,16 @@
|
|||
-include("emqx_authz.hrl").
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
-include_lib("common_test/include/ct.hrl").
|
||||
-include_lib("emqx/include/emqx_placeholder.hrl").
|
||||
|
||||
-define(SOURCE1, {deny, all}).
|
||||
-define(SOURCE2, {allow, {ipaddr, "127.0.0.1"}, all, [{eq, "#"}, {eq, "+"}]}).
|
||||
-define(SOURCE3, {allow, {ipaddrs, ["127.0.0.1", "192.168.1.0/24"]}, subscribe, ["%c"]}).
|
||||
-define(SOURCE3, {allow, {ipaddrs, ["127.0.0.1", "192.168.1.0/24"]}, subscribe, [?PH_S_CLIENTID]}).
|
||||
-define(SOURCE4, {allow, {'and', [{client, "test"}, {user, "test"}]}, publish, ["topic/test"]}).
|
||||
-define(SOURCE5, {allow, {'or',
|
||||
[{username, {re, "^test"}},
|
||||
{clientid, {re, "test?"}}]},
|
||||
publish, ["%u", "%c"]}).
|
||||
publish, [?PH_S_USERNAME, ?PH_S_CLIENTID]}).
|
||||
|
||||
all() ->
|
||||
emqx_common_test_helpers:all(?MODULE).
|
||||
|
@ -67,7 +68,7 @@ t_compile(_) ->
|
|||
{ipaddrs,[{{127,0,0,1},{127,0,0,1},32},
|
||||
{{192,168,1,0},{192,168,1,255},24}]},
|
||||
subscribe,
|
||||
[{pattern,[<<"%c">>]}]
|
||||
[{pattern,[?PH_CLIENTID]}]
|
||||
}, emqx_authz_rule:compile(?SOURCE3)),
|
||||
|
||||
?assertMatch({allow,
|
||||
|
@ -79,7 +80,7 @@ t_compile(_) ->
|
|||
?assertMatch({allow,
|
||||
{'or', [{username, {re_pattern, _, _, _, _}},
|
||||
{clientid, {re_pattern, _, _, _, _}}]},
|
||||
publish, [{pattern, [<<"%u">>]}, {pattern, [<<"%c">>]}]
|
||||
publish, [{pattern, [?PH_USERNAME]}, {pattern, [?PH_CLIENTID]}]
|
||||
}, emqx_authz_rule:compile(?SOURCE5)),
|
||||
ok.
|
||||
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
%%--------------------------------------------------------------------
|
||||
-module(emqx_auto_subscribe_placeholder).
|
||||
|
||||
-include_lib("emqx/include/emqx_placeholder.hrl").
|
||||
|
||||
-export([generate/1]).
|
||||
|
||||
-export([to_topic_table/3]).
|
||||
|
@ -40,13 +42,13 @@ to_topic_table(PHs, ClientInfo, ConnInfo) ->
|
|||
|
||||
generate(<<"">>, Result) ->
|
||||
lists:reverse(Result);
|
||||
generate(<<"${clientid}", Tail/binary>>, Result) ->
|
||||
generate(<<?PH_S_CLIENTID, Tail/binary>>, Result) ->
|
||||
generate(Tail, [clientid | Result]);
|
||||
generate(<<"${username}", Tail/binary>>, Result) ->
|
||||
generate(<<?PH_S_USERNAME, Tail/binary>>, Result) ->
|
||||
generate(Tail, [username | Result]);
|
||||
generate(<<"${host}", Tail/binary>>, Result) ->
|
||||
generate(<<?PH_S_HOST, Tail/binary>>, Result) ->
|
||||
generate(Tail, [host | Result]);
|
||||
generate(<<"${port}", Tail/binary>>, Result) ->
|
||||
generate(<<?PH_S_PORT, Tail/binary>>, Result) ->
|
||||
generate(Tail, [port | Result]);
|
||||
generate(<<Char:8, Tail/binary>>, []) ->
|
||||
generate(Tail, [<<Char:8>>]);
|
||||
|
@ -62,7 +64,7 @@ to_topic([Binary | PTs], C, Co, Res) when is_binary(Binary) ->
|
|||
to_topic([clientid | PTs], C = #{clientid := ClientID}, Co, Res) ->
|
||||
to_topic(PTs, C, Co, [ClientID | Res]);
|
||||
to_topic([username | PTs], C = #{username := undefined}, Co, Res) ->
|
||||
to_topic(PTs, C, Co, [<<"${username}">> | Res]);
|
||||
to_topic(PTs, C, Co, [?PH_USERNAME | Res]);
|
||||
to_topic([username | PTs], C = #{username := Username}, Co, Res) ->
|
||||
to_topic(PTs, C, Co, [Username | Res]);
|
||||
to_topic([host | PTs], C, Co = #{peername := {Host, _}}, Res) ->
|
||||
|
|
|
@ -35,7 +35,7 @@ gateway.lwm2m {
|
|||
lifetime_max = 86400s
|
||||
qmode_time_window = 22
|
||||
auto_observe = false
|
||||
mountpoint = \"lwm2m/%u\"
|
||||
mountpoint = \"lwm2m/${username}\"
|
||||
update_msg_publish_condition = contains_object_list
|
||||
translators {
|
||||
command = {topic = \"/dn/#\", qos = 0}
|
||||
|
|
|
@ -35,7 +35,7 @@ gateway.lwm2m {
|
|||
lifetime_max = 86400s
|
||||
qmode_time_window = 200
|
||||
auto_observe = false
|
||||
mountpoint = \"lwm2m/%u\"
|
||||
mountpoint = \"lwm2m/${username}\"
|
||||
update_msg_publish_condition = contains_object_list
|
||||
translators {
|
||||
command = {topic = \"/dn/#\", qos = 0}
|
||||
|
|
Loading…
Reference in New Issue