feat: support initialize client attribute from user property
This commit is contained in:
parent
2fd0a2cd4d
commit
8254b801ae
|
@ -263,7 +263,8 @@ init(
|
||||||
},
|
},
|
||||||
Zone
|
Zone
|
||||||
),
|
),
|
||||||
ClientInfo = initialize_client_attrs_from_cert(ClientInfo0, Peercert, Zone),
|
AttrExtractionConfig = get_mqtt_conf(Zone, client_attrs_init),
|
||||||
|
ClientInfo = initialize_client_attrs_from_cert(AttrExtractionConfig, ClientInfo0, Peercert),
|
||||||
{NClientInfo, NConnInfo} = take_ws_cookie(ClientInfo, ConnInfo),
|
{NClientInfo, NConnInfo} = take_ws_cookie(ClientInfo, ConnInfo),
|
||||||
#channel{
|
#channel{
|
||||||
conninfo = NConnInfo,
|
conninfo = NConnInfo,
|
||||||
|
@ -1576,30 +1577,31 @@ enrich_client(ConnPkt, Channel = #channel{clientinfo = ClientInfo}) ->
|
||||||
{error, ReasonCode, Channel#channel{clientinfo = NClientInfo}}
|
{error, ReasonCode, Channel#channel{clientinfo = NClientInfo}}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
initialize_client_attrs_from_cert(ClientInfo, Peercert, Zone) ->
|
initialize_client_attrs_from_cert(
|
||||||
case get_mqtt_conf(Zone, client_attrs_init) of
|
#{
|
||||||
#{
|
extract_from := From,
|
||||||
extract_from := From,
|
extract_regexp := Regexp,
|
||||||
extract_regexp := Regexp,
|
extract_as := AttrName
|
||||||
extract_as := AttrName
|
},
|
||||||
} when From =:= cn orelse From =:= dn ->
|
ClientInfo,
|
||||||
case extract_client_attr_from_cert(From, Regexp, Peercert) of
|
Peercert
|
||||||
{ok, Value} ->
|
) when From =:= cn orelse From =:= dn ->
|
||||||
?SLOG(
|
case extract_client_attr_from_cert(From, Regexp, Peercert) of
|
||||||
debug,
|
{ok, Value} ->
|
||||||
#{
|
?SLOG(
|
||||||
msg => "client_attr_init_from_cert",
|
debug,
|
||||||
extracted_as => AttrName,
|
#{
|
||||||
extracted_value => Value
|
msg => "client_attr_init_from_cert",
|
||||||
}
|
extracted_as => AttrName,
|
||||||
),
|
extracted_value => Value
|
||||||
ClientInfo#{client_attrs => #{AttrName => Value}};
|
}
|
||||||
_ ->
|
),
|
||||||
ClientInfo#{client_attrs => #{}}
|
ClientInfo#{client_attrs => #{AttrName => Value}};
|
||||||
end;
|
|
||||||
_ ->
|
_ ->
|
||||||
ClientInfo#{client_attrs => #{}}
|
ClientInfo#{client_attrs => #{}}
|
||||||
end.
|
end;
|
||||||
|
initialize_client_attrs_from_cert(_, ClientInfo, _Peercert) ->
|
||||||
|
ClientInfo.
|
||||||
|
|
||||||
extract_client_attr_from_cert(cn, Regexp, Peercert) ->
|
extract_client_attr_from_cert(cn, Regexp, Peercert) ->
|
||||||
CN = esockd_peercert:common_name(Peercert),
|
CN = esockd_peercert:common_name(Peercert),
|
||||||
|
@ -1656,9 +1658,10 @@ maybe_assign_clientid(#mqtt_packet_connect{clientid = <<>>}, ClientInfo) ->
|
||||||
maybe_assign_clientid(#mqtt_packet_connect{clientid = ClientId}, ClientInfo) ->
|
maybe_assign_clientid(#mqtt_packet_connect{clientid = ClientId}, ClientInfo) ->
|
||||||
{ok, ClientInfo#{clientid => ClientId}}.
|
{ok, ClientInfo#{clientid => ClientId}}.
|
||||||
|
|
||||||
maybe_set_client_initial_attr(_, #{zone := Zone} = ClientInfo) ->
|
maybe_set_client_initial_attr(ConnPkt, #{zone := Zone} = ClientInfo0) ->
|
||||||
Attrs = maps:get(client_attrs, ClientInfo, #{}),
|
|
||||||
Config = get_mqtt_conf(Zone, client_attrs_init),
|
Config = get_mqtt_conf(Zone, client_attrs_init),
|
||||||
|
ClientInfo = initialize_client_attrs_from_user_property(Config, ConnPkt, ClientInfo0),
|
||||||
|
Attrs = maps:get(client_attrs, ClientInfo, #{}),
|
||||||
case extract_attr_from_clientinfo(Config, ClientInfo) of
|
case extract_attr_from_clientinfo(Config, ClientInfo) of
|
||||||
{ok, Value} ->
|
{ok, Value} ->
|
||||||
#{extract_as := Name} = Config,
|
#{extract_as := Name} = Config,
|
||||||
|
@ -1675,6 +1678,43 @@ maybe_set_client_initial_attr(_, #{zone := Zone} = ClientInfo) ->
|
||||||
{ok, ClientInfo}
|
{ok, ClientInfo}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
initialize_client_attrs_from_user_property(
|
||||||
|
#{
|
||||||
|
extract_from := user_property,
|
||||||
|
extract_as := PropertyKey
|
||||||
|
},
|
||||||
|
ConnPkt,
|
||||||
|
ClientInfo
|
||||||
|
) ->
|
||||||
|
case extract_client_attr_from_user_property(ConnPkt, PropertyKey) of
|
||||||
|
{ok, Value} ->
|
||||||
|
?SLOG(
|
||||||
|
debug,
|
||||||
|
#{
|
||||||
|
msg => "client_attr_init_from_user_property",
|
||||||
|
extracted_as => PropertyKey,
|
||||||
|
extracted_value => Value
|
||||||
|
}
|
||||||
|
),
|
||||||
|
ClientInfo#{client_attrs => #{PropertyKey => Value}};
|
||||||
|
_ ->
|
||||||
|
ClientInfo
|
||||||
|
end;
|
||||||
|
initialize_client_attrs_from_user_property(_, _ConnInfo, ClientInfo) ->
|
||||||
|
ClientInfo.
|
||||||
|
|
||||||
|
extract_client_attr_from_user_property(
|
||||||
|
#mqtt_packet_connect{properties = #{'User-Property' := UserProperty}}, PropertyKey
|
||||||
|
) ->
|
||||||
|
case lists:keyfind(PropertyKey, 1, UserProperty) of
|
||||||
|
{_, Value} ->
|
||||||
|
{ok, Value};
|
||||||
|
_ ->
|
||||||
|
not_found
|
||||||
|
end;
|
||||||
|
extract_client_attr_from_user_property(_ConnPkt, _PropertyKey) ->
|
||||||
|
ignored.
|
||||||
|
|
||||||
extract_attr_from_clientinfo(#{extract_from := clientid, extract_regexp := Regexp}, #{
|
extract_attr_from_clientinfo(#{extract_from := clientid, extract_regexp := Regexp}, #{
|
||||||
clientid := ClientId
|
clientid := ClientId
|
||||||
}) ->
|
}) ->
|
||||||
|
|
|
@ -1736,8 +1736,8 @@ fields("client_attrs_init") ->
|
||||||
[
|
[
|
||||||
{extract_from,
|
{extract_from,
|
||||||
sc(
|
sc(
|
||||||
hoconsc:enum([clientid, username, cn, dn]),
|
hoconsc:enum([clientid, username, cn, dn, user_property]),
|
||||||
#{desc => ?DESC("client_atrs_init_extract_from")}
|
#{desc => ?DESC("client_attrs_init_extract_from")}
|
||||||
)},
|
)},
|
||||||
{extract_regexp, sc(binary(), #{desc => ?DESC("client_attrs_init_extract_regexp")})},
|
{extract_regexp, sc(binary(), #{desc => ?DESC("client_attrs_init_extract_regexp")})},
|
||||||
{extract_as,
|
{extract_as,
|
||||||
|
@ -2010,6 +2010,8 @@ desc("session_persistence") ->
|
||||||
"Settings governing durable sessions persistence.";
|
"Settings governing durable sessions persistence.";
|
||||||
desc(durable_storage) ->
|
desc(durable_storage) ->
|
||||||
?DESC(durable_storage);
|
?DESC(durable_storage);
|
||||||
|
desc("client_attrs_init") ->
|
||||||
|
?DESC(client_attrs_init);
|
||||||
desc(_) ->
|
desc(_) ->
|
||||||
undefined.
|
undefined.
|
||||||
|
|
||||||
|
|
|
@ -77,6 +77,7 @@ groups() ->
|
||||||
t_username_as_clientid,
|
t_username_as_clientid,
|
||||||
t_certcn_as_alias,
|
t_certcn_as_alias,
|
||||||
t_certdn_as_alias,
|
t_certdn_as_alias,
|
||||||
|
t_client_attr_from_user_property,
|
||||||
t_certcn_as_clientid_default_config_tls,
|
t_certcn_as_clientid_default_config_tls,
|
||||||
t_certcn_as_clientid_tlsv1_3,
|
t_certcn_as_clientid_tlsv1_3,
|
||||||
t_certcn_as_clientid_tlsv1_2,
|
t_certcn_as_clientid_tlsv1_2,
|
||||||
|
@ -408,10 +409,33 @@ test_cert_extraction_as_alias(Which) ->
|
||||||
{ok, _} = emqtt:connect(Client),
|
{ok, _} = emqtt:connect(Client),
|
||||||
%% assert only two chars are extracted
|
%% assert only two chars are extracted
|
||||||
?assertMatch(
|
?assertMatch(
|
||||||
#{clientinfo := #{client_attrs := #{alias := <<_, _>>}}}, emqx_cm:get_chan_info(ClientId)
|
#{clientinfo := #{client_attrs := #{<<"alias">> := <<_, _>>}}},
|
||||||
|
emqx_cm:get_chan_info(ClientId)
|
||||||
),
|
),
|
||||||
emqtt:disconnect(Client).
|
emqtt:disconnect(Client).
|
||||||
|
|
||||||
|
t_client_attr_from_user_property(_Config) ->
|
||||||
|
ClientId = atom_to_binary(?FUNCTION_NAME),
|
||||||
|
emqx_config:put_zone_conf(default, [mqtt, client_attrs_init], #{
|
||||||
|
extract_from => user_property,
|
||||||
|
extract_as => <<"group">>
|
||||||
|
}),
|
||||||
|
SslConf = emqx_common_test_helpers:client_mtls('tlsv1.3'),
|
||||||
|
{ok, Client} = emqtt:start_link([
|
||||||
|
{clientid, ClientId},
|
||||||
|
{port, 8883},
|
||||||
|
{ssl, true},
|
||||||
|
{ssl_opts, SslConf},
|
||||||
|
{proto_ver, v5},
|
||||||
|
{properties, #{'User-Property' => [{<<"group">>, <<"g1">>}]}}
|
||||||
|
]),
|
||||||
|
{ok, _} = emqtt:connect(Client),
|
||||||
|
%% assert only two chars are extracted
|
||||||
|
?assertMatch(
|
||||||
|
#{clientinfo := #{client_attrs := #{<<"group">> := <<"g1">>}}},
|
||||||
|
emqx_cm:get_chan_info(ClientId)
|
||||||
|
),
|
||||||
|
emqtt:disconnect(Client).
|
||||||
t_certcn_as_clientid_default_config_tls(_) ->
|
t_certcn_as_clientid_default_config_tls(_) ->
|
||||||
tls_certcn_as_clientid(default).
|
tls_certcn_as_clientid(default).
|
||||||
|
|
||||||
|
|
|
@ -1592,12 +1592,14 @@ client_attrs_init_extract_from {
|
||||||
- `username`: Extract from the username.
|
- `username`: Extract from the username.
|
||||||
- `cn`: Extract from the Common Name (CN) field of the client certificate.
|
- `cn`: Extract from the Common Name (CN) field of the client certificate.
|
||||||
- `dn`: Extract from the Distinguished Name (DN) field of the client certficate.
|
- `dn`: Extract from the Distinguished Name (DN) field of the client certficate.
|
||||||
|
- `user_property`: Extract from the user property sent in the MQTT v5 `CONNECT` packet.
|
||||||
|
In this case, `extract_regex` is not applicable, and `extract_as` should be the user property key.
|
||||||
|
|
||||||
NOTE: this extraction happens **after** `clientid` or `username` is initialized
|
NOTE: this extraction happens **after** `clientid` or `username` is initialized
|
||||||
from `peer_cert_as_clientid` or `peer_cert_as_username` config."""
|
from `peer_cert_as_clientid` or `peer_cert_as_username` config."""
|
||||||
}
|
}
|
||||||
|
|
||||||
client_attrs_init_extract_regex {
|
client_attrs_init_extract_regexp {
|
||||||
label: "Client attribute extract regex"
|
label: "Client attribute extract regex"
|
||||||
desc: """~
|
desc: """~
|
||||||
The regular expression to extract a client attribute from the client property specified by `client_attrs_init.extract_from` config.
|
The regular expression to extract a client attribute from the client property specified by `client_attrs_init.extract_from` config.
|
||||||
|
@ -1610,7 +1612,8 @@ client_attrs_init_extract_as {
|
||||||
label: "Name the extracted attribute"
|
label: "Name the extracted attribute"
|
||||||
desc: """~
|
desc: """~
|
||||||
The name of the client attribute extracted from the client property specified by `client_attrs_init.extract_from` config.
|
The name of the client attribute extracted from the client property specified by `client_attrs_init.extract_from` config.
|
||||||
The extracted attribute will be stored in the `client_attr` property with this name."""
|
The extracted attribute will be stored in the `client_attr` property with this name.
|
||||||
|
In case `extract_from = user_property`, this should be the key of the user property."""
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue