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:
DDDHuang 2021-11-23 10:56:43 +08:00 committed by GitHub
parent 6fb464fc05
commit 21bd9bba55
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 270 additions and 173 deletions

View File

@ -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

View File

@ -20,57 +20,93 @@
-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.

View File

@ -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).

View File

@ -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
})).

View File

@ -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">>
}

View File

@ -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.

View File

@ -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.

View File

@ -4,6 +4,7 @@
"password_hash":"c5e46903df45e5dc096dc74657610dbee8deaacae656df88a1788f1847390242",
"salt": "e378187547bf2d6f0545a3f441aa4d8a",
"is_superuser": true
,
{
"user_id":"myuser2",

View File

@ -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) ->

View File

@ -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}),
@ -82,9 +85,8 @@ t_authn(_) ->
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}).

View File

@ -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) ->

View File

@ -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">>
},

View File

@ -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}).

View File

@ -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
@ -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);

View File

@ -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.

View File

@ -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,15 +90,15 @@ 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([?PH_S_CERT_CN_NAME | Params], ClientInfo, Acc) ->
replvar(Params, ClientInfo, [safe_get(cn, ClientInfo) | Acc]);
replvar(["'%d'" | Params], 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.

View File

@ -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,15 +95,15 @@ 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([?PH_S_CERT_CN_NAME | Params], ClientInfo, Acc) ->
replvar(Params, ClientInfo, [safe_get(cn, ClientInfo) | Acc]);
replvar(["'%d'" | Params], 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.

View File

@ -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.

View File

@ -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.
@ -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 = #{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}, [<<"%u">>|Words], Acc) ->
feed_var(ClientInfo, Words, [<<"%u">>|Acc]);
feed_var(ClientInfo = #{username := Username}, [<<"%u">>|Words], 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]).

View File

@ -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,

View File

@ -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,

View File

@ -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 #">>}
]
}),

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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) ->

View File

@ -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}

View File

@ -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}