feat: support initialize client attribute from user property
This commit is contained in:
parent
2fd0a2cd4d
commit
8254b801ae
|
@ -263,7 +263,8 @@ init(
|
|||
},
|
||||
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),
|
||||
#channel{
|
||||
conninfo = NConnInfo,
|
||||
|
@ -1576,30 +1577,31 @@ enrich_client(ConnPkt, Channel = #channel{clientinfo = ClientInfo}) ->
|
|||
{error, ReasonCode, Channel#channel{clientinfo = NClientInfo}}
|
||||
end.
|
||||
|
||||
initialize_client_attrs_from_cert(ClientInfo, Peercert, Zone) ->
|
||||
case get_mqtt_conf(Zone, client_attrs_init) of
|
||||
#{
|
||||
extract_from := From,
|
||||
extract_regexp := Regexp,
|
||||
extract_as := AttrName
|
||||
} when From =:= cn orelse From =:= dn ->
|
||||
case extract_client_attr_from_cert(From, Regexp, Peercert) of
|
||||
{ok, Value} ->
|
||||
?SLOG(
|
||||
debug,
|
||||
#{
|
||||
msg => "client_attr_init_from_cert",
|
||||
extracted_as => AttrName,
|
||||
extracted_value => Value
|
||||
}
|
||||
),
|
||||
ClientInfo#{client_attrs => #{AttrName => Value}};
|
||||
_ ->
|
||||
ClientInfo#{client_attrs => #{}}
|
||||
end;
|
||||
initialize_client_attrs_from_cert(
|
||||
#{
|
||||
extract_from := From,
|
||||
extract_regexp := Regexp,
|
||||
extract_as := AttrName
|
||||
},
|
||||
ClientInfo,
|
||||
Peercert
|
||||
) when From =:= cn orelse From =:= dn ->
|
||||
case extract_client_attr_from_cert(From, Regexp, Peercert) of
|
||||
{ok, Value} ->
|
||||
?SLOG(
|
||||
debug,
|
||||
#{
|
||||
msg => "client_attr_init_from_cert",
|
||||
extracted_as => AttrName,
|
||||
extracted_value => Value
|
||||
}
|
||||
),
|
||||
ClientInfo#{client_attrs => #{AttrName => Value}};
|
||||
_ ->
|
||||
ClientInfo#{client_attrs => #{}}
|
||||
end.
|
||||
end;
|
||||
initialize_client_attrs_from_cert(_, ClientInfo, _Peercert) ->
|
||||
ClientInfo.
|
||||
|
||||
extract_client_attr_from_cert(cn, Regexp, 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) ->
|
||||
{ok, ClientInfo#{clientid => ClientId}}.
|
||||
|
||||
maybe_set_client_initial_attr(_, #{zone := Zone} = ClientInfo) ->
|
||||
Attrs = maps:get(client_attrs, ClientInfo, #{}),
|
||||
maybe_set_client_initial_attr(ConnPkt, #{zone := Zone} = ClientInfo0) ->
|
||||
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
|
||||
{ok, Value} ->
|
||||
#{extract_as := Name} = Config,
|
||||
|
@ -1675,6 +1678,43 @@ maybe_set_client_initial_attr(_, #{zone := Zone} = ClientInfo) ->
|
|||
{ok, ClientInfo}
|
||||
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}, #{
|
||||
clientid := ClientId
|
||||
}) ->
|
||||
|
|
|
@ -1736,8 +1736,8 @@ fields("client_attrs_init") ->
|
|||
[
|
||||
{extract_from,
|
||||
sc(
|
||||
hoconsc:enum([clientid, username, cn, dn]),
|
||||
#{desc => ?DESC("client_atrs_init_extract_from")}
|
||||
hoconsc:enum([clientid, username, cn, dn, user_property]),
|
||||
#{desc => ?DESC("client_attrs_init_extract_from")}
|
||||
)},
|
||||
{extract_regexp, sc(binary(), #{desc => ?DESC("client_attrs_init_extract_regexp")})},
|
||||
{extract_as,
|
||||
|
@ -2010,6 +2010,8 @@ desc("session_persistence") ->
|
|||
"Settings governing durable sessions persistence.";
|
||||
desc(durable_storage) ->
|
||||
?DESC(durable_storage);
|
||||
desc("client_attrs_init") ->
|
||||
?DESC(client_attrs_init);
|
||||
desc(_) ->
|
||||
undefined.
|
||||
|
||||
|
|
|
@ -77,6 +77,7 @@ groups() ->
|
|||
t_username_as_clientid,
|
||||
t_certcn_as_alias,
|
||||
t_certdn_as_alias,
|
||||
t_client_attr_from_user_property,
|
||||
t_certcn_as_clientid_default_config_tls,
|
||||
t_certcn_as_clientid_tlsv1_3,
|
||||
t_certcn_as_clientid_tlsv1_2,
|
||||
|
@ -408,10 +409,33 @@ test_cert_extraction_as_alias(Which) ->
|
|||
{ok, _} = emqtt:connect(Client),
|
||||
%% assert only two chars are extracted
|
||||
?assertMatch(
|
||||
#{clientinfo := #{client_attrs := #{alias := <<_, _>>}}}, emqx_cm:get_chan_info(ClientId)
|
||||
#{clientinfo := #{client_attrs := #{<<"alias">> := <<_, _>>}}},
|
||||
emqx_cm:get_chan_info(ClientId)
|
||||
),
|
||||
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(_) ->
|
||||
tls_certcn_as_clientid(default).
|
||||
|
||||
|
|
|
@ -1592,12 +1592,14 @@ client_attrs_init_extract_from {
|
|||
- `username`: Extract from the username.
|
||||
- `cn`: Extract from the Common Name (CN) field of the client certificate.
|
||||
- `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
|
||||
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"
|
||||
desc: """~
|
||||
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"
|
||||
desc: """~
|
||||
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