Merge pull request #11392 from lafirest/feat/ldap_authz
feat(ldap-authz): integrate the LDAP authorization
This commit is contained in:
commit
2b03436552
|
@ -19,7 +19,8 @@
|
||||||
-export([
|
-export([
|
||||||
hash/2,
|
hash/2,
|
||||||
hash_data/2,
|
hash_data/2,
|
||||||
check_pass/3
|
check_pass/3,
|
||||||
|
compare_secure/2
|
||||||
]).
|
]).
|
||||||
|
|
||||||
-export_type([
|
-export_type([
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
%% Copyright (c) 2022-2023 EMQ Technologies Co., Ltd. All Rights Reserved.
|
%% Copyright (c) 2023 EMQ Technologies Co., Ltd. All Rights Reserved.
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
-module(emqx_authn_enterprise).
|
-module(emqx_authn_enterprise).
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
%% -*- mode: erlang -*-
|
%% -*- mode: erlang -*-
|
||||||
{application, emqx_authz, [
|
{application, emqx_authz, [
|
||||||
{description, "An OTP application"},
|
{description, "An OTP application"},
|
||||||
{vsn, "0.1.24"},
|
{vsn, "0.1.25"},
|
||||||
{registered, []},
|
{registered, []},
|
||||||
{mod, {emqx_authz_app, []}},
|
{mod, {emqx_authz_app, []}},
|
||||||
{applications, [
|
{applications, [
|
||||||
|
|
|
@ -19,6 +19,8 @@
|
||||||
-behaviour(emqx_config_handler).
|
-behaviour(emqx_config_handler).
|
||||||
-behaviour(emqx_config_backup).
|
-behaviour(emqx_config_backup).
|
||||||
|
|
||||||
|
-dialyzer({nowarn_function, [authz_module/1]}).
|
||||||
|
|
||||||
-include("emqx_authz.hrl").
|
-include("emqx_authz.hrl").
|
||||||
-include_lib("emqx/include/logger.hrl").
|
-include_lib("emqx/include/logger.hrl").
|
||||||
-include_lib("emqx/include/emqx_hooks.hrl").
|
-include_lib("emqx/include/emqx_hooks.hrl").
|
||||||
|
@ -571,7 +573,12 @@ find_action_in_hooks() ->
|
||||||
authz_module(built_in_database) ->
|
authz_module(built_in_database) ->
|
||||||
emqx_authz_mnesia;
|
emqx_authz_mnesia;
|
||||||
authz_module(Type) ->
|
authz_module(Type) ->
|
||||||
list_to_existing_atom("emqx_authz_" ++ atom_to_list(Type)).
|
case emqx_authz_enterprise:is_enterprise_module(Type) of
|
||||||
|
{ok, Module} ->
|
||||||
|
Module;
|
||||||
|
_ ->
|
||||||
|
list_to_existing_atom("emqx_authz_" ++ atom_to_list(Type))
|
||||||
|
end.
|
||||||
|
|
||||||
type(#{type := Type}) -> type(Type);
|
type(#{type := Type}) -> type(Type);
|
||||||
type(#{<<"type">> := Type}) -> type(Type);
|
type(#{<<"type">> := Type}) -> type(Type);
|
||||||
|
@ -591,8 +598,7 @@ type(built_in_database) -> built_in_database;
|
||||||
type(<<"built_in_database">>) -> built_in_database;
|
type(<<"built_in_database">>) -> built_in_database;
|
||||||
type(client_info) -> client_info;
|
type(client_info) -> client_info;
|
||||||
type(<<"client_info">>) -> client_info;
|
type(<<"client_info">>) -> client_info;
|
||||||
%% should never happen if the input is type-checked by hocon schema
|
type(MaybeEnterprise) -> emqx_authz_enterprise:type(MaybeEnterprise).
|
||||||
type(Unknown) -> throw({unknown_authz_source_type, Unknown}).
|
|
||||||
|
|
||||||
maybe_write_files(#{<<"type">> := <<"file">>} = Source) ->
|
maybe_write_files(#{<<"type">> := <<"file">>} = Source) ->
|
||||||
write_acl_file(Source);
|
write_acl_file(Source);
|
||||||
|
|
|
@ -95,7 +95,9 @@ fields(position) ->
|
||||||
in => body
|
in => body
|
||||||
}
|
}
|
||||||
)}
|
)}
|
||||||
].
|
];
|
||||||
|
fields(MaybeEnterprise) ->
|
||||||
|
emqx_authz_enterprise:fields(MaybeEnterprise).
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
%% http type funcs
|
%% http type funcs
|
||||||
|
@ -283,7 +285,7 @@ authz_sources_types(Type) ->
|
||||||
mysql,
|
mysql,
|
||||||
postgresql,
|
postgresql,
|
||||||
file
|
file
|
||||||
].
|
] ++ emqx_authz_enterprise:authz_sources_types().
|
||||||
|
|
||||||
to_list(A) when is_atom(A) ->
|
to_list(A) when is_atom(A) ->
|
||||||
atom_to_list(A);
|
atom_to_list(A);
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Copyright (c) 2023 EMQ Technologies Co., Ltd. All Rights Reserved.
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
-module(emqx_authz_enterprise).
|
||||||
|
|
||||||
|
-export([
|
||||||
|
type_names/0,
|
||||||
|
fields/1,
|
||||||
|
is_enterprise_module/1,
|
||||||
|
authz_sources_types/0,
|
||||||
|
type/1,
|
||||||
|
desc/1
|
||||||
|
]).
|
||||||
|
|
||||||
|
-if(?EMQX_RELEASE_EDITION == ee).
|
||||||
|
|
||||||
|
%% type name set
|
||||||
|
type_names() ->
|
||||||
|
[ldap].
|
||||||
|
|
||||||
|
%% type -> type schema
|
||||||
|
fields(ldap) ->
|
||||||
|
emqx_ldap_authz:fields(config).
|
||||||
|
|
||||||
|
%% type -> type module
|
||||||
|
is_enterprise_module(ldap) ->
|
||||||
|
{ok, emqx_ldap_authz};
|
||||||
|
is_enterprise_module(_) ->
|
||||||
|
false.
|
||||||
|
|
||||||
|
%% api sources set
|
||||||
|
authz_sources_types() ->
|
||||||
|
[ldap].
|
||||||
|
|
||||||
|
%% atom-able name -> type
|
||||||
|
type(<<"ldap">>) -> ldap;
|
||||||
|
type(ldap) -> ldap;
|
||||||
|
type(Unknown) -> throw({unknown_authz_source_type, Unknown}).
|
||||||
|
|
||||||
|
desc(ldap) ->
|
||||||
|
emqx_ldap_authz:description();
|
||||||
|
desc(_) ->
|
||||||
|
undefined.
|
||||||
|
|
||||||
|
-else.
|
||||||
|
|
||||||
|
-dialyzer({nowarn_function, [fields/1, type/1, desc/1]}).
|
||||||
|
|
||||||
|
type_names() ->
|
||||||
|
[].
|
||||||
|
|
||||||
|
fields(Any) ->
|
||||||
|
error({invalid_field, Any}).
|
||||||
|
|
||||||
|
is_enterprise_module(_) ->
|
||||||
|
false.
|
||||||
|
|
||||||
|
authz_sources_types() ->
|
||||||
|
[].
|
||||||
|
|
||||||
|
%% should never happen if the input is type-checked by hocon schema
|
||||||
|
type(Unknown) -> throw({unknown_authz_source_type, Unknown}).
|
||||||
|
|
||||||
|
desc(_) ->
|
||||||
|
undefined.
|
||||||
|
-endif.
|
|
@ -43,7 +43,8 @@
|
||||||
-export([
|
-export([
|
||||||
headers_no_content_type/1,
|
headers_no_content_type/1,
|
||||||
headers/1,
|
headers/1,
|
||||||
default_authz/0
|
default_authz/0,
|
||||||
|
authz_common_fields/1
|
||||||
]).
|
]).
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
@ -64,7 +65,8 @@ type_names() ->
|
||||||
redis_single,
|
redis_single,
|
||||||
redis_sentinel,
|
redis_sentinel,
|
||||||
redis_cluster
|
redis_cluster
|
||||||
].
|
] ++
|
||||||
|
emqx_authz_enterprise:type_names().
|
||||||
|
|
||||||
namespace() -> authz.
|
namespace() -> authz.
|
||||||
|
|
||||||
|
@ -176,7 +178,9 @@ fields("node_error") ->
|
||||||
[
|
[
|
||||||
node_name(),
|
node_name(),
|
||||||
{"error", ?HOCON(string(), #{desc => ?DESC("node_error")})}
|
{"error", ?HOCON(string(), #{desc => ?DESC("node_error")})}
|
||||||
].
|
];
|
||||||
|
fields(MaybeEnterprise) ->
|
||||||
|
emqx_authz_enterprise:fields(MaybeEnterprise).
|
||||||
|
|
||||||
common_field() ->
|
common_field() ->
|
||||||
[
|
[
|
||||||
|
@ -220,8 +224,8 @@ desc(redis_sentinel) ->
|
||||||
?DESC(redis_sentinel);
|
?DESC(redis_sentinel);
|
||||||
desc(redis_cluster) ->
|
desc(redis_cluster) ->
|
||||||
?DESC(redis_cluster);
|
?DESC(redis_cluster);
|
||||||
desc(_) ->
|
desc(MaybeEnterprise) ->
|
||||||
undefined.
|
emqx_authz_enterprise:desc(MaybeEnterprise).
|
||||||
|
|
||||||
authz_common_fields(Type) ->
|
authz_common_fields(Type) ->
|
||||||
[
|
[
|
||||||
|
|
|
@ -4,5 +4,6 @@
|
||||||
{deps, [
|
{deps, [
|
||||||
{emqx_connector, {path, "../../apps/emqx_connector"}},
|
{emqx_connector, {path, "../../apps/emqx_connector"}},
|
||||||
{emqx_resource, {path, "../../apps/emqx_resource"}},
|
{emqx_resource, {path, "../../apps/emqx_resource"}},
|
||||||
{emqx_authn, {path, "../../apps/emqx_authn"}}
|
{emqx_authn, {path, "../../apps/emqx_authn"}},
|
||||||
|
{emqx_authz, {path, "../../apps/emqx_authz"}}
|
||||||
]}.
|
]}.
|
||||||
|
|
|
@ -5,7 +5,8 @@
|
||||||
{applications, [
|
{applications, [
|
||||||
kernel,
|
kernel,
|
||||||
stdlib,
|
stdlib,
|
||||||
emqx_authn
|
emqx_authn,
|
||||||
|
emqx_authz
|
||||||
]},
|
]},
|
||||||
{env, []},
|
{env, []},
|
||||||
{modules, []},
|
{modules, []},
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
%% Copyright (c) 2022-2023 EMQ Technologies Co., Ltd. All Rights Reserved.
|
%% Copyright (c) 2023 EMQ Technologies Co., Ltd. All Rights Reserved.
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
-module(emqx_ldap_authn).
|
-module(emqx_ldap_authn).
|
||||||
|
@ -47,7 +47,7 @@ tags() ->
|
||||||
|
|
||||||
%% used for config check when the schema module is resolved
|
%% used for config check when the schema module is resolved
|
||||||
roots() ->
|
roots() ->
|
||||||
[{?CONF_NS, hoconsc:mk(hoconsc:ref(?MODULE, mysql))}].
|
[{?CONF_NS, hoconsc:mk(hoconsc:ref(?MODULE, ldap))}].
|
||||||
|
|
||||||
fields(ldap) ->
|
fields(ldap) ->
|
||||||
[
|
[
|
||||||
|
@ -73,7 +73,7 @@ is_superuser_attribute(desc) -> ?DESC(?FUNCTION_NAME);
|
||||||
is_superuser_attribute(default) -> <<"isSuperuser">>;
|
is_superuser_attribute(default) -> <<"isSuperuser">>;
|
||||||
is_superuser_attribute(_) -> undefined.
|
is_superuser_attribute(_) -> undefined.
|
||||||
|
|
||||||
query_timeout(type) -> emqx_schema:duration_ms();
|
query_timeout(type) -> emqx_schema:timeout_duration_ms();
|
||||||
query_timeout(desc) -> ?DESC(?FUNCTION_NAME);
|
query_timeout(desc) -> ?DESC(?FUNCTION_NAME);
|
||||||
query_timeout(default) -> <<"5s">>;
|
query_timeout(default) -> <<"5s">>;
|
||||||
query_timeout(_) -> undefined.
|
query_timeout(_) -> undefined.
|
||||||
|
@ -173,7 +173,7 @@ ensure_password(
|
||||||
undefined ->
|
undefined ->
|
||||||
{error, no_password};
|
{error, no_password};
|
||||||
[LDAPPassword | _] ->
|
[LDAPPassword | _] ->
|
||||||
extract_hash_algorithm(LDAPPassword, Password, fun try_decode_passowrd/4, Entry, State)
|
extract_hash_algorithm(LDAPPassword, Password, fun try_decode_password/4, Entry, State)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%% RFC 2307 format password
|
%% RFC 2307 format password
|
||||||
|
@ -207,7 +207,7 @@ is_valid_algorithm(HashType, PasswordHash, Password, Entry, State) ->
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%% this password is in LDIF format which is base64 encoding
|
%% this password is in LDIF format which is base64 encoding
|
||||||
try_decode_passowrd(LDAPPassword, Password, Entry, State) ->
|
try_decode_password(LDAPPassword, Password, Entry, State) ->
|
||||||
case safe_base64_decode(LDAPPassword) of
|
case safe_base64_decode(LDAPPassword) of
|
||||||
{ok, Decode} ->
|
{ok, Decode} ->
|
||||||
extract_hash_algorithm(
|
extract_hash_algorithm(
|
||||||
|
@ -279,9 +279,7 @@ hash_password(Algorithm, Salt, suffix, Password) ->
|
||||||
hash_password(Algorithm, Data) ->
|
hash_password(Algorithm, Data) ->
|
||||||
crypto:hash(Algorithm, Data).
|
crypto:hash(Algorithm, Data).
|
||||||
|
|
||||||
compare_password(hash, PasswordHash, PasswordHash) ->
|
compare_password(hash, LDAPPasswordHash, PasswordHash) ->
|
||||||
true;
|
emqx_passwd:compare_secure(LDAPPasswordHash, PasswordHash);
|
||||||
compare_password(base64, Base64HashData, PasswordHash) ->
|
compare_password(base64, Base64HashData, PasswordHash) ->
|
||||||
Base64HashData =:= base64:encode(PasswordHash);
|
emqx_passwd:compare_secure(Base64HashData, base64:encode(PasswordHash)).
|
||||||
compare_password(_, _, _) ->
|
|
||||||
false.
|
|
||||||
|
|
|
@ -0,0 +1,164 @@
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Copyright (c) 2020-2023 EMQ Technologies Co., Ltd. All Rights Reserved.
|
||||||
|
%%
|
||||||
|
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
%% you may not use this file except in compliance with the License.
|
||||||
|
%% You may obtain a copy of the License at
|
||||||
|
%%
|
||||||
|
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
%%
|
||||||
|
%% Unless required by applicable law or agreed to in writing, software
|
||||||
|
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
%% See the License for the specific language governing permissions and
|
||||||
|
%% limitations under the License.
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
|
-module(emqx_ldap_authz).
|
||||||
|
|
||||||
|
-include_lib("emqx_authz/include/emqx_authz.hrl").
|
||||||
|
-include_lib("emqx/include/emqx.hrl").
|
||||||
|
-include_lib("hocon/include/hoconsc.hrl").
|
||||||
|
-include_lib("emqx/include/logger.hrl").
|
||||||
|
-include_lib("emqx/include/emqx_placeholder.hrl").
|
||||||
|
-include_lib("eldap/include/eldap.hrl").
|
||||||
|
|
||||||
|
-behaviour(emqx_authz).
|
||||||
|
|
||||||
|
-define(PREPARE_KEY, ?MODULE).
|
||||||
|
|
||||||
|
%% AuthZ Callbacks
|
||||||
|
-export([
|
||||||
|
description/0,
|
||||||
|
create/1,
|
||||||
|
update/1,
|
||||||
|
destroy/1,
|
||||||
|
authorize/4
|
||||||
|
]).
|
||||||
|
|
||||||
|
-export([fields/1]).
|
||||||
|
|
||||||
|
-ifdef(TEST).
|
||||||
|
-compile(export_all).
|
||||||
|
-compile(nowarn_export_all).
|
||||||
|
-endif.
|
||||||
|
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
%% Hocon Schema
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
fields(config) ->
|
||||||
|
emqx_authz_schema:authz_common_fields(ldap) ++
|
||||||
|
[
|
||||||
|
{publish_attribute, attribute_meta(publish_attribute, <<"mqttPublishTopic">>)},
|
||||||
|
{subscribe_attribute, attribute_meta(subscribe_attribute, <<"mqttSubscriptionTopic">>)},
|
||||||
|
{all_attribute, attribute_meta(all_attribute, <<"mqttPubSubTopic">>)},
|
||||||
|
{query_timeout,
|
||||||
|
?HOCON(
|
||||||
|
emqx_schema:timeout_duration_ms(),
|
||||||
|
#{
|
||||||
|
desc => ?DESC(query_timeout),
|
||||||
|
default => <<"5s">>
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
] ++
|
||||||
|
emqx_ldap:fields(config).
|
||||||
|
|
||||||
|
attribute_meta(Name, Default) ->
|
||||||
|
?HOCON(
|
||||||
|
string(),
|
||||||
|
#{
|
||||||
|
default => Default,
|
||||||
|
desc => ?DESC(Name)
|
||||||
|
}
|
||||||
|
).
|
||||||
|
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
%% AuthZ Callbacks
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
description() ->
|
||||||
|
"AuthZ with LDAP".
|
||||||
|
|
||||||
|
create(Source) ->
|
||||||
|
ResourceId = emqx_authz_utils:make_resource_id(?MODULE),
|
||||||
|
{ok, _Data} = emqx_authz_utils:create_resource(ResourceId, emqx_ldap, Source),
|
||||||
|
Annotations = new_annotations(#{id => ResourceId}, Source),
|
||||||
|
Source#{annotations => Annotations}.
|
||||||
|
|
||||||
|
update(Source) ->
|
||||||
|
case emqx_authz_utils:update_resource(emqx_ldap, Source) of
|
||||||
|
{error, Reason} ->
|
||||||
|
error({load_config_error, Reason});
|
||||||
|
{ok, Id} ->
|
||||||
|
Annotations = new_annotations(#{id => Id}, Source),
|
||||||
|
Source#{annotations => Annotations}
|
||||||
|
end.
|
||||||
|
|
||||||
|
destroy(#{annotations := #{id := Id}}) ->
|
||||||
|
ok = emqx_resource:remove_local(Id).
|
||||||
|
|
||||||
|
authorize(
|
||||||
|
Client,
|
||||||
|
Action,
|
||||||
|
Topic,
|
||||||
|
#{
|
||||||
|
query_timeout := QueryTimeout,
|
||||||
|
annotations := #{id := ResourceID} = Annotations
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
Attrs = select_attrs(Action, Annotations),
|
||||||
|
case emqx_resource:simple_sync_query(ResourceID, {query, Client, Attrs, QueryTimeout}) of
|
||||||
|
{ok, []} ->
|
||||||
|
nomatch;
|
||||||
|
{ok, [Entry | _]} ->
|
||||||
|
do_authorize(Action, Topic, Attrs, Entry);
|
||||||
|
{error, Reason} ->
|
||||||
|
?SLOG(error, #{
|
||||||
|
msg => "query_ldap_error",
|
||||||
|
reason => Reason,
|
||||||
|
resource_id => ResourceID
|
||||||
|
}),
|
||||||
|
nomatch
|
||||||
|
end.
|
||||||
|
|
||||||
|
do_authorize(Action, Topic, [Attr | T], Entry) ->
|
||||||
|
Topics = proplists:get_value(Attr, Entry#eldap_entry.attributes, []),
|
||||||
|
case match_topic(Topic, Topics) of
|
||||||
|
true ->
|
||||||
|
{matched, allow};
|
||||||
|
false ->
|
||||||
|
do_authorize(Action, Topic, T, Entry)
|
||||||
|
end;
|
||||||
|
do_authorize(_Action, _Topic, [], _Entry) ->
|
||||||
|
nomatch.
|
||||||
|
|
||||||
|
new_annotations(Init, Source) ->
|
||||||
|
lists:foldl(
|
||||||
|
fun(Attr, Acc) ->
|
||||||
|
Acc#{
|
||||||
|
Attr =>
|
||||||
|
case maps:get(Attr, Source) of
|
||||||
|
Value when is_binary(Value) ->
|
||||||
|
erlang:binary_to_list(Value);
|
||||||
|
Value ->
|
||||||
|
Value
|
||||||
|
end
|
||||||
|
}
|
||||||
|
end,
|
||||||
|
Init,
|
||||||
|
[publish_attribute, subscribe_attribute, all_attribute]
|
||||||
|
).
|
||||||
|
|
||||||
|
select_attrs(#{action_type := publish}, #{publish_attribute := Pub, all_attribute := All}) ->
|
||||||
|
[Pub, All];
|
||||||
|
select_attrs(_, #{subscribe_attribute := Sub, all_attribute := All}) ->
|
||||||
|
[Sub, All].
|
||||||
|
|
||||||
|
match_topic(Target, Topics) ->
|
||||||
|
lists:any(
|
||||||
|
fun(Topic) ->
|
||||||
|
emqx_topic:match(Target, erlang:list_to_binary(Topic))
|
||||||
|
end,
|
||||||
|
Topics
|
||||||
|
).
|
|
@ -27,5 +27,5 @@ dn : {token, {dn, TokenLine}}.
|
||||||
Erlang code.
|
Erlang code.
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
%% Copyright (c) 2022-2023 EMQ Technologies Co., Ltd. All Rights Reserved.
|
%% Copyright (c) 2023 EMQ Technologies Co., Ltd. All Rights Reserved.
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
Header "%%--------------------------------------------------------------------
|
Header "%%--------------------------------------------------------------------
|
||||||
%% Copyright (c) 2022-2023 EMQ Technologies Co., Ltd. All Rights Reserved.
|
%% Copyright (c) 2023 EMQ Technologies Co., Ltd. All Rights Reserved.
|
||||||
%%--------------------------------------------------------------------".
|
%%--------------------------------------------------------------------".
|
||||||
|
|
||||||
Nonterminals
|
Nonterminals
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
%% Copyright (c) 2022-2023 EMQ Technologies Co., Ltd. All Rights Reserved.
|
%% Copyright (c) 2023 EMQ Technologies Co., Ltd. All Rights Reserved.
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
-module(emqx_ldap_SUITE).
|
-module(emqx_ldap_SUITE).
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
%% Copyright (c) 2022-2023 EMQ Technologies Co., Ltd. All Rights Reserved.
|
%% Copyright (c) 2023 EMQ Technologies Co., Ltd. All Rights Reserved.
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
-module(emqx_ldap_authn_SUITE).
|
-module(emqx_ldap_authn_SUITE).
|
||||||
|
|
||||||
-compile(nowarn_export_all).
|
-compile(nowarn_export_all).
|
||||||
|
|
|
@ -0,0 +1,173 @@
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Copyright (c) 2023 EMQ Technologies Co., Ltd. All Rights Reserved.
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
-module(emqx_ldap_authz_SUITE).
|
||||||
|
|
||||||
|
-compile(nowarn_export_all).
|
||||||
|
-compile(export_all).
|
||||||
|
|
||||||
|
-include("emqx_authz.hrl").
|
||||||
|
-include_lib("eunit/include/eunit.hrl").
|
||||||
|
-include_lib("common_test/include/ct.hrl").
|
||||||
|
|
||||||
|
-define(LDAP_HOST, "ldap").
|
||||||
|
-define(LDAP_DEFAULT_PORT, 389).
|
||||||
|
-define(LDAP_RESOURCE, <<"emqx_ldap_authz_SUITE">>).
|
||||||
|
|
||||||
|
all() ->
|
||||||
|
emqx_authz_test_lib:all_with_table_case(?MODULE, t_run_case, cases()).
|
||||||
|
|
||||||
|
groups() ->
|
||||||
|
emqx_authz_test_lib:table_groups(t_run_case, cases()).
|
||||||
|
|
||||||
|
init_per_suite(Config) ->
|
||||||
|
ok = stop_apps([emqx_resource]),
|
||||||
|
case emqx_common_test_helpers:is_tcp_server_available(?LDAP_HOST, ?LDAP_DEFAULT_PORT) of
|
||||||
|
true ->
|
||||||
|
ok = emqx_common_test_helpers:start_apps(
|
||||||
|
[emqx_conf, emqx_authz],
|
||||||
|
fun set_special_configs/1
|
||||||
|
),
|
||||||
|
ok = start_apps([emqx_resource]),
|
||||||
|
ok = create_ldap_resource(),
|
||||||
|
Config;
|
||||||
|
false ->
|
||||||
|
{skip, no_ldap}
|
||||||
|
end.
|
||||||
|
|
||||||
|
end_per_suite(_Config) ->
|
||||||
|
ok = emqx_authz_test_lib:restore_authorizers(),
|
||||||
|
ok = emqx_resource:remove_local(?LDAP_RESOURCE),
|
||||||
|
ok = stop_apps([emqx_resource]),
|
||||||
|
ok = emqx_common_test_helpers:stop_apps([emqx_conf, emqx_authz]).
|
||||||
|
|
||||||
|
init_per_group(Group, Config) ->
|
||||||
|
[{test_case, emqx_authz_test_lib:get_case(Group, cases())} | Config].
|
||||||
|
end_per_group(_Group, _Config) ->
|
||||||
|
ok.
|
||||||
|
|
||||||
|
init_per_testcase(_TestCase, Config) ->
|
||||||
|
ok = emqx_authz_test_lib:reset_authorizers(),
|
||||||
|
Config.
|
||||||
|
end_per_testcase(_TestCase, _Config) ->
|
||||||
|
_ = emqx_authz:set_feature_available(rich_actions, true),
|
||||||
|
ok.
|
||||||
|
|
||||||
|
set_special_configs(emqx_authz) ->
|
||||||
|
ok = emqx_authz_test_lib:reset_authorizers();
|
||||||
|
set_special_configs(_) ->
|
||||||
|
ok.
|
||||||
|
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
%% Testcases
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
t_run_case(Config) ->
|
||||||
|
Case = ?config(test_case, Config),
|
||||||
|
ok = setup_authz_source(),
|
||||||
|
ok = emqx_authz_test_lib:run_checks(Case).
|
||||||
|
|
||||||
|
t_create_invalid(_Config) ->
|
||||||
|
ok = setup_authz_source(),
|
||||||
|
BadConfig = maps:merge(
|
||||||
|
raw_ldap_authz_config(),
|
||||||
|
#{<<"server">> => <<"255.255.255.255:33333">>}
|
||||||
|
),
|
||||||
|
{ok, _} = emqx_authz:update(?CMD_REPLACE, [BadConfig]),
|
||||||
|
|
||||||
|
[_] = emqx_authz:lookup().
|
||||||
|
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
%% Case
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
cases() ->
|
||||||
|
[
|
||||||
|
#{
|
||||||
|
name => simpe_publish,
|
||||||
|
client_info => #{username => <<"mqttuser0001">>},
|
||||||
|
checks => [
|
||||||
|
{allow, ?AUTHZ_PUBLISH, <<"mqttuser0001/pub/1">>},
|
||||||
|
{allow, ?AUTHZ_PUBLISH, <<"mqttuser0001/pub/+">>},
|
||||||
|
{allow, ?AUTHZ_PUBLISH, <<"mqttuser0001/pub/#">>}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
#{
|
||||||
|
name => simpe_subscribe,
|
||||||
|
client_info => #{username => <<"mqttuser0001">>},
|
||||||
|
checks => [
|
||||||
|
{allow, ?AUTHZ_SUBSCRIBE, <<"mqttuser0001/sub/1">>},
|
||||||
|
{allow, ?AUTHZ_SUBSCRIBE, <<"mqttuser0001/sub/+">>},
|
||||||
|
{allow, ?AUTHZ_SUBSCRIBE, <<"mqttuser0001/sub/#">>}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
#{
|
||||||
|
name => simpe_pubsub,
|
||||||
|
client_info => #{username => <<"mqttuser0001">>},
|
||||||
|
checks => [
|
||||||
|
{allow, ?AUTHZ_PUBLISH, <<"mqttuser0001/pubsub/1">>},
|
||||||
|
{allow, ?AUTHZ_PUBLISH, <<"mqttuser0001/pubsub/+">>},
|
||||||
|
{allow, ?AUTHZ_PUBLISH, <<"mqttuser0001/pubsub/#">>},
|
||||||
|
|
||||||
|
{allow, ?AUTHZ_SUBSCRIBE, <<"mqttuser0001/pubsub/1">>},
|
||||||
|
{allow, ?AUTHZ_SUBSCRIBE, <<"mqttuser0001/pubsub/+">>},
|
||||||
|
{allow, ?AUTHZ_SUBSCRIBE, <<"mqttuser0001/pubsub/#">>}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
#{
|
||||||
|
name => simpe_unmatched,
|
||||||
|
client_info => #{username => <<"mqttuser0001">>},
|
||||||
|
checks => [
|
||||||
|
{deny, ?AUTHZ_PUBLISH, <<"mqttuser0001/req/mqttuser0001/+">>},
|
||||||
|
{deny, ?AUTHZ_PUBLISH, <<"mqttuser0001/req/mqttuser0002/+">>},
|
||||||
|
{deny, ?AUTHZ_SUBSCRIBE, <<"mqttuser0001/req/+/mqttuser0002">>}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
].
|
||||||
|
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
%% Helpers
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
setup_authz_source() ->
|
||||||
|
setup_config(#{}).
|
||||||
|
|
||||||
|
raw_ldap_authz_config() ->
|
||||||
|
#{
|
||||||
|
<<"enable">> => <<"true">>,
|
||||||
|
<<"type">> => <<"ldap">>,
|
||||||
|
<<"server">> => ldap_server(),
|
||||||
|
<<"base_object">> => <<"uid=${username},ou=testdevice,dc=emqx,dc=io">>,
|
||||||
|
<<"username">> => <<"cn=root,dc=emqx,dc=io">>,
|
||||||
|
<<"password">> => <<"public">>,
|
||||||
|
<<"pool_size">> => 8
|
||||||
|
}.
|
||||||
|
|
||||||
|
setup_config(SpecialParams) ->
|
||||||
|
emqx_authz_test_lib:setup_config(
|
||||||
|
raw_ldap_authz_config(),
|
||||||
|
SpecialParams
|
||||||
|
).
|
||||||
|
|
||||||
|
ldap_server() ->
|
||||||
|
iolist_to_binary(io_lib:format("~s:~B", [?LDAP_HOST, ?LDAP_DEFAULT_PORT])).
|
||||||
|
|
||||||
|
ldap_config() ->
|
||||||
|
emqx_ldap_SUITE:ldap_config([]).
|
||||||
|
|
||||||
|
start_apps(Apps) ->
|
||||||
|
lists:foreach(fun application:ensure_all_started/1, Apps).
|
||||||
|
|
||||||
|
stop_apps(Apps) ->
|
||||||
|
lists:foreach(fun application:stop/1, Apps).
|
||||||
|
|
||||||
|
create_ldap_resource() ->
|
||||||
|
{ok, _} = emqx_resource:create_local(
|
||||||
|
?LDAP_RESOURCE,
|
||||||
|
?RESOURCE_GROUP,
|
||||||
|
emqx_ldap,
|
||||||
|
ldap_config(),
|
||||||
|
#{}
|
||||||
|
),
|
||||||
|
ok.
|
|
@ -1,5 +1,5 @@
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
%% Copyright (c) 2022-2023 EMQ Technologies Co., Ltd. All Rights Reserved.
|
%% Copyright (c) 2023 EMQ Technologies Co., Ltd. All Rights Reserved.
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
-module(emqx_ldap_filter_SUITE).
|
-module(emqx_ldap_filter_SUITE).
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Integrated the LDAP as a new authenticator.
|
Integrated LDAP as a new authenticator.
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Integrated LDAP as a authorization source.
|
|
@ -0,0 +1,27 @@
|
||||||
|
emqx_ldap_authz {
|
||||||
|
|
||||||
|
publish_attribute.desc:
|
||||||
|
"""Indicates which attribute is used to represent the allowed topics list of the `publish`."""
|
||||||
|
|
||||||
|
publish_attribute.label:
|
||||||
|
"""Publish Attribute"""
|
||||||
|
|
||||||
|
subscribe_attribute.desc:
|
||||||
|
"""Indicates which attribute is used to represent the allowed topics list of the `subscribe`."""
|
||||||
|
|
||||||
|
subscribe_attribute.label:
|
||||||
|
"""Subscribe Attribute"""
|
||||||
|
|
||||||
|
all_attribute.desc:
|
||||||
|
"""Indicates which attribute is used to represent the both allowed topics list of `publish` and `subscribe`."""
|
||||||
|
|
||||||
|
all_attribute.label:
|
||||||
|
"""All Attribute"""
|
||||||
|
|
||||||
|
query_timeout.desc:
|
||||||
|
"""Timeout for the LDAP query."""
|
||||||
|
|
||||||
|
query_timeout.label:
|
||||||
|
"""Query Timeout"""
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue