refactor(client_attr): allow more than one initial extraction
This commit is contained in:
parent
2577224bc7
commit
da5b01aa46
|
@ -269,8 +269,7 @@ init(
|
||||||
},
|
},
|
||||||
Zone
|
Zone
|
||||||
),
|
),
|
||||||
AttrExtractionConfig = get_mqtt_conf(Zone, client_attrs_init),
|
ClientInfo = initialize_client_attrs_from_cert(ClientInfo0, Peercert),
|
||||||
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,
|
||||||
|
@ -1575,7 +1574,7 @@ enrich_client(ConnPkt, Channel = #channel{clientinfo = ClientInfo}) ->
|
||||||
fun maybe_username_as_clientid/2,
|
fun maybe_username_as_clientid/2,
|
||||||
fun maybe_assign_clientid/2,
|
fun maybe_assign_clientid/2,
|
||||||
%% attr init should happen after clientid and username assign
|
%% attr init should happen after clientid and username assign
|
||||||
fun maybe_set_client_initial_attr/2
|
fun maybe_set_client_initial_attrs/2
|
||||||
],
|
],
|
||||||
ConnPkt,
|
ConnPkt,
|
||||||
ClientInfo
|
ClientInfo
|
||||||
|
@ -1587,7 +1586,17 @@ 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(
|
initialize_client_attrs_from_cert(#{zone := Zone} = ClientInfo, Peercert) ->
|
||||||
|
Inits = get_client_attrs_init_config(Zone),
|
||||||
|
lists:foldl(
|
||||||
|
fun(Init, Acc) ->
|
||||||
|
do_initialize_client_attrs_from_cert(Init, Acc, Peercert)
|
||||||
|
end,
|
||||||
|
ClientInfo,
|
||||||
|
Inits
|
||||||
|
).
|
||||||
|
|
||||||
|
do_initialize_client_attrs_from_cert(
|
||||||
#{
|
#{
|
||||||
extract_from := From,
|
extract_from := From,
|
||||||
extract_regexp := Regexp,
|
extract_regexp := Regexp,
|
||||||
|
@ -1596,21 +1605,24 @@ initialize_client_attrs_from_cert(
|
||||||
ClientInfo,
|
ClientInfo,
|
||||||
Peercert
|
Peercert
|
||||||
) when From =:= cn orelse From =:= dn ->
|
) when From =:= cn orelse From =:= dn ->
|
||||||
case extract_client_attr_from_cert(From, Regexp, Peercert) of
|
Attrs0 = maps:get(client_attrs, ClientInfo, #{}),
|
||||||
{ok, Value} ->
|
Attrs =
|
||||||
?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 => #{}}
|
Attrs0#{AttrName => Value};
|
||||||
end;
|
_ ->
|
||||||
initialize_client_attrs_from_cert(_, ClientInfo, _Peercert) ->
|
Attrs0
|
||||||
|
end,
|
||||||
|
ClientInfo#{client_attrs => Attrs};
|
||||||
|
do_initialize_client_attrs_from_cert(_, ClientInfo, _Peercert) ->
|
||||||
ClientInfo.
|
ClientInfo.
|
||||||
|
|
||||||
extract_client_attr_from_cert(cn, Regexp, Peercert) ->
|
extract_client_attr_from_cert(cn, Regexp, Peercert) ->
|
||||||
|
@ -1668,27 +1680,51 @@ 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(ConnPkt, #{zone := Zone} = ClientInfo0) ->
|
get_client_attrs_init_config(Zone) ->
|
||||||
Config = get_mqtt_conf(Zone, client_attrs_init),
|
case get_mqtt_conf(Zone, client_attrs_init, []) of
|
||||||
ClientInfo = initialize_client_attrs_from_user_property(Config, ConnPkt, ClientInfo0),
|
L when is_list(L) -> L;
|
||||||
Attrs = maps:get(client_attrs, ClientInfo, #{}),
|
M when is_map(M) -> [M]
|
||||||
case extract_attr_from_clientinfo(Config, ClientInfo) of
|
|
||||||
{ok, Value} ->
|
|
||||||
#{extract_as := Name} = Config,
|
|
||||||
?SLOG(
|
|
||||||
debug,
|
|
||||||
#{
|
|
||||||
msg => "client_attr_init_from_clientinfo",
|
|
||||||
extracted_as => Name,
|
|
||||||
extracted_value => Value
|
|
||||||
}
|
|
||||||
),
|
|
||||||
{ok, ClientInfo#{client_attrs => Attrs#{Name => Value}}};
|
|
||||||
_ ->
|
|
||||||
{ok, ClientInfo}
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
initialize_client_attrs_from_user_property(
|
maybe_set_client_initial_attrs(ConnPkt, #{zone := Zone} = ClientInfo0) ->
|
||||||
|
Inits = get_client_attrs_init_config(Zone),
|
||||||
|
ClientInfo = initialize_client_attrs_from_user_property(Inits, ConnPkt, ClientInfo0),
|
||||||
|
{ok, initialize_client_attrs_from_clientinfo(Inits, ClientInfo)}.
|
||||||
|
|
||||||
|
initialize_client_attrs_from_clientinfo(Inits, ClientInfo) ->
|
||||||
|
lists:foldl(
|
||||||
|
fun(Init, Acc) ->
|
||||||
|
Attrs = maps:get(client_attrs, ClientInfo, #{}),
|
||||||
|
case extract_attr_from_clientinfo(Init, ClientInfo) of
|
||||||
|
{ok, Value} ->
|
||||||
|
#{extract_as := Name} = Init,
|
||||||
|
?SLOG(
|
||||||
|
debug,
|
||||||
|
#{
|
||||||
|
msg => "client_attr_init_from_clientinfo",
|
||||||
|
extracted_as => Name,
|
||||||
|
extracted_value => Value
|
||||||
|
}
|
||||||
|
),
|
||||||
|
Acc#{client_attrs => Attrs#{Name => Value}};
|
||||||
|
_ ->
|
||||||
|
Acc
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
ClientInfo,
|
||||||
|
Inits
|
||||||
|
).
|
||||||
|
|
||||||
|
initialize_client_attrs_from_user_property(Inits, ConnPkt, ClientInfo) ->
|
||||||
|
lists:foldl(
|
||||||
|
fun(Init, Acc) ->
|
||||||
|
do_initialize_client_attrs_from_user_property(Init, ConnPkt, Acc)
|
||||||
|
end,
|
||||||
|
ClientInfo,
|
||||||
|
Inits
|
||||||
|
).
|
||||||
|
|
||||||
|
do_initialize_client_attrs_from_user_property(
|
||||||
#{
|
#{
|
||||||
extract_from := user_property,
|
extract_from := user_property,
|
||||||
extract_as := PropertyKey
|
extract_as := PropertyKey
|
||||||
|
@ -1696,21 +1732,24 @@ initialize_client_attrs_from_user_property(
|
||||||
ConnPkt,
|
ConnPkt,
|
||||||
ClientInfo
|
ClientInfo
|
||||||
) ->
|
) ->
|
||||||
case extract_client_attr_from_user_property(ConnPkt, PropertyKey) of
|
Attrs0 = maps:get(client_attrs, ClientInfo, #{}),
|
||||||
{ok, Value} ->
|
Attrs =
|
||||||
?SLOG(
|
case extract_client_attr_from_user_property(ConnPkt, PropertyKey) of
|
||||||
debug,
|
{ok, Value} ->
|
||||||
#{
|
?SLOG(
|
||||||
msg => "client_attr_init_from_user_property",
|
debug,
|
||||||
extracted_as => PropertyKey,
|
#{
|
||||||
extracted_value => Value
|
msg => "client_attr_init_from_user_property",
|
||||||
}
|
extracted_as => PropertyKey,
|
||||||
),
|
extracted_value => Value
|
||||||
ClientInfo#{client_attrs => #{PropertyKey => Value}};
|
}
|
||||||
_ ->
|
),
|
||||||
ClientInfo
|
Attrs0#{PropertyKey => Value};
|
||||||
end;
|
_ ->
|
||||||
initialize_client_attrs_from_user_property(_, _ConnInfo, ClientInfo) ->
|
Attrs0
|
||||||
|
end,
|
||||||
|
ClientInfo#{client_attrs => Attrs};
|
||||||
|
do_initialize_client_attrs_from_user_property(_, _ConnPkt, ClientInfo) ->
|
||||||
ClientInfo.
|
ClientInfo.
|
||||||
|
|
||||||
extract_client_attr_from_user_property(
|
extract_client_attr_from_user_property(
|
||||||
|
|
|
@ -3552,9 +3552,9 @@ mqtt_general() ->
|
||||||
)},
|
)},
|
||||||
{"client_attrs_init",
|
{"client_attrs_init",
|
||||||
sc(
|
sc(
|
||||||
hoconsc:union([disabled, ref("client_attrs_init")]),
|
hoconsc:union([hoconsc:array(ref("client_attrs_init")), ref("client_attrs_init")]),
|
||||||
#{
|
#{
|
||||||
default => disabled,
|
default => [],
|
||||||
desc => ?DESC("client_attrs_init")
|
desc => ?DESC("client_attrs_init")
|
||||||
}
|
}
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -454,7 +454,7 @@ zone_global_defaults() ->
|
||||||
upgrade_qos => false,
|
upgrade_qos => false,
|
||||||
use_username_as_clientid => false,
|
use_username_as_clientid => false,
|
||||||
wildcard_subscription => true,
|
wildcard_subscription => true,
|
||||||
client_attrs_init => disabled
|
client_attrs_init => []
|
||||||
},
|
},
|
||||||
overload_protection =>
|
overload_protection =>
|
||||||
#{
|
#{
|
||||||
|
|
|
@ -170,7 +170,7 @@ t_client_attr_as_mountpoint(_Config) ->
|
||||||
?assertMatch([_], emqx_router:match_routes(MatchTopic)),
|
?assertMatch([_], emqx_router:match_routes(MatchTopic)),
|
||||||
emqtt:stop(Client)
|
emqtt:stop(Client)
|
||||||
end),
|
end),
|
||||||
emqx_config:put_zone_conf(default, [mqtt, client_attrs_init], disabled),
|
emqx_config:put_zone_conf(default, [mqtt, client_attrs_init], []),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
t_current_conns_tcp(_Config) ->
|
t_current_conns_tcp(_Config) ->
|
||||||
|
|
|
@ -572,7 +572,7 @@ t_alias_prefix(_Config) ->
|
||||||
?assertMatch({ok, _, [?RC_NOT_AUTHORIZED]}, emqtt:subscribe(C, SubTopicNotAllowed)),
|
?assertMatch({ok, _, [?RC_NOT_AUTHORIZED]}, emqtt:subscribe(C, SubTopicNotAllowed)),
|
||||||
unlink(C),
|
unlink(C),
|
||||||
emqtt:stop(C),
|
emqtt:stop(C),
|
||||||
emqx_config:put_zone_conf(default, [mqtt, client_attrs_init], disalbed),
|
emqx_config:put_zone_conf(default, [mqtt, client_attrs_init], []),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
%% client is allowed by ACL to publish to its LWT topic, is connected,
|
%% client is allowed by ACL to publish to its LWT topic, is connected,
|
||||||
|
|
|
@ -7,7 +7,7 @@ an MQTT connection.
|
||||||
|
|
||||||
### Initialization of `client_attrs`
|
### Initialization of `client_attrs`
|
||||||
|
|
||||||
- The `client_attrs` field can be initially populated based on the configuration from one of the
|
- The `client_attrs` fields can be initially populated based on the configuration from one of the
|
||||||
following sources:
|
following sources:
|
||||||
- `cn`: The common name from the TLS client's certificate.
|
- `cn`: The common name from the TLS client's certificate.
|
||||||
- `dn`: The distinguished name from the TLS client's certificate, that is, the certificate "Subject".
|
- `dn`: The distinguished name from the TLS client's certificate, that is, the certificate "Subject".
|
||||||
|
|
|
@ -1575,7 +1575,8 @@ client_attrs_init {
|
||||||
label: "Client Attributes Initialization"
|
label: "Client Attributes Initialization"
|
||||||
desc: """~
|
desc: """~
|
||||||
Specify how to initialize client attributes.
|
Specify how to initialize client attributes.
|
||||||
One initial client attribute can be initialized as `client_attrs.NAME`,
|
This config accepts one initialization rule, or a list of rules.
|
||||||
|
Client attributes can be initialized as `client_attrs.NAME`,
|
||||||
where `NAME` is the name of the attribute specified in the config `extract_as`.
|
where `NAME` is the name of the attribute specified in the config `extract_as`.
|
||||||
The initialized client attribute will be stored in the `client_attrs` property with the specified name,
|
The initialized client attribute will be stored in the `client_attrs` property with the specified name,
|
||||||
and can be used as a placeholder in a template for authentication and authorization.
|
and can be used as a placeholder in a template for authentication and authorization.
|
||||||
|
|
Loading…
Reference in New Issue