From 7055eafb91a704f581e1562f76a219fe1663a800 Mon Sep 17 00:00:00 2001 From: firest Date: Fri, 4 Aug 2023 10:44:25 +0800 Subject: [PATCH 1/3] fix(ldap): fix license date and some minor problems --- apps/emqx/src/emqx_passwd.erl | 3 ++- apps/emqx_authn/src/emqx_authn_enterprise.erl | 2 +- apps/emqx_ldap/src/emqx_ldap_authn.erl | 18 ++++++++---------- apps/emqx_ldap/src/emqx_ldap_filter_lexer.xrl | 2 +- apps/emqx_ldap/src/emqx_ldap_filter_parser.yrl | 2 +- apps/emqx_ldap/test/emqx_ldap_SUITE.erl | 2 +- apps/emqx_ldap/test/emqx_ldap_authn_SUITE.erl | 2 +- apps/emqx_ldap/test/emqx_ldap_filter_SUITE.erl | 2 +- changes/ee/feat-11386.en.md | 2 +- rel/i18n/emqx_ldap_authn.hocon | 2 +- 10 files changed, 18 insertions(+), 19 deletions(-) diff --git a/apps/emqx/src/emqx_passwd.erl b/apps/emqx/src/emqx_passwd.erl index dc940645b..c68a146ed 100644 --- a/apps/emqx/src/emqx_passwd.erl +++ b/apps/emqx/src/emqx_passwd.erl @@ -19,7 +19,8 @@ -export([ hash/2, hash_data/2, - check_pass/3 + check_pass/3, + compare_secure/2 ]). -export_type([ diff --git a/apps/emqx_authn/src/emqx_authn_enterprise.erl b/apps/emqx_authn/src/emqx_authn_enterprise.erl index b50ec2c17..029872694 100644 --- a/apps/emqx_authn/src/emqx_authn_enterprise.erl +++ b/apps/emqx_authn/src/emqx_authn_enterprise.erl @@ -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). diff --git a/apps/emqx_ldap/src/emqx_ldap_authn.erl b/apps/emqx_ldap/src/emqx_ldap_authn.erl index c7bf61cd2..d814e2aae 100644 --- a/apps/emqx_ldap/src/emqx_ldap_authn.erl +++ b/apps/emqx_ldap/src/emqx_ldap_authn.erl @@ -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). @@ -47,7 +47,7 @@ tags() -> %% used for config check when the schema module is resolved roots() -> - [{?CONF_NS, hoconsc:mk(hoconsc:ref(?MODULE, mysql))}]. + [{?CONF_NS, hoconsc:mk(hoconsc:ref(?MODULE, ldap))}]. fields(ldap) -> [ @@ -73,7 +73,7 @@ is_superuser_attribute(desc) -> ?DESC(?FUNCTION_NAME); is_superuser_attribute(default) -> <<"isSuperuser">>; 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(default) -> <<"5s">>; query_timeout(_) -> undefined. @@ -173,7 +173,7 @@ ensure_password( undefined -> {error, no_password}; [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. %% RFC 2307 format password @@ -207,7 +207,7 @@ is_valid_algorithm(HashType, PasswordHash, Password, Entry, State) -> end. %% 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 {ok, Decode} -> extract_hash_algorithm( @@ -279,9 +279,7 @@ hash_password(Algorithm, Salt, suffix, Password) -> hash_password(Algorithm, Data) -> crypto:hash(Algorithm, Data). -compare_password(hash, PasswordHash, PasswordHash) -> - true; +compare_password(hash, LDAPPasswordHash, PasswordHash) -> + emqx_passwd:compare_secure(LDAPPasswordHash, PasswordHash); compare_password(base64, Base64HashData, PasswordHash) -> - Base64HashData =:= base64:encode(PasswordHash); -compare_password(_, _, _) -> - false. + emqx_passwd:compare_secure(Base64HashData, base64:encode(PasswordHash)). diff --git a/apps/emqx_ldap/src/emqx_ldap_filter_lexer.xrl b/apps/emqx_ldap/src/emqx_ldap_filter_lexer.xrl index 6d75c2546..a82a3ee3e 100644 --- a/apps/emqx_ldap/src/emqx_ldap_filter_lexer.xrl +++ b/apps/emqx_ldap/src/emqx_ldap_filter_lexer.xrl @@ -27,5 +27,5 @@ dn : {token, {dn, TokenLine}}. Erlang code. %%-------------------------------------------------------------------- -%% Copyright (c) 2022-2023 EMQ Technologies Co., Ltd. All Rights Reserved. +%% Copyright (c) 2023 EMQ Technologies Co., Ltd. All Rights Reserved. %%-------------------------------------------------------------------- diff --git a/apps/emqx_ldap/src/emqx_ldap_filter_parser.yrl b/apps/emqx_ldap/src/emqx_ldap_filter_parser.yrl index 57f526ffd..e1b1ed98e 100644 --- a/apps/emqx_ldap/src/emqx_ldap_filter_parser.yrl +++ b/apps/emqx_ldap/src/emqx_ldap_filter_parser.yrl @@ -1,5 +1,5 @@ Header "%%-------------------------------------------------------------------- -%% Copyright (c) 2022-2023 EMQ Technologies Co., Ltd. All Rights Reserved. +%% Copyright (c) 2023 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------". Nonterminals diff --git a/apps/emqx_ldap/test/emqx_ldap_SUITE.erl b/apps/emqx_ldap/test/emqx_ldap_SUITE.erl index bf20629ec..a191da3bd 100644 --- a/apps/emqx_ldap/test/emqx_ldap_SUITE.erl +++ b/apps/emqx_ldap/test/emqx_ldap_SUITE.erl @@ -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). diff --git a/apps/emqx_ldap/test/emqx_ldap_authn_SUITE.erl b/apps/emqx_ldap/test/emqx_ldap_authn_SUITE.erl index ce7481ef8..d984cc89f 100644 --- a/apps/emqx_ldap/test/emqx_ldap_authn_SUITE.erl +++ b/apps/emqx_ldap/test/emqx_ldap_authn_SUITE.erl @@ -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_SUITE). diff --git a/apps/emqx_ldap/test/emqx_ldap_filter_SUITE.erl b/apps/emqx_ldap/test/emqx_ldap_filter_SUITE.erl index 0351fb93d..1a7e970a8 100644 --- a/apps/emqx_ldap/test/emqx_ldap_filter_SUITE.erl +++ b/apps/emqx_ldap/test/emqx_ldap_filter_SUITE.erl @@ -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). diff --git a/changes/ee/feat-11386.en.md b/changes/ee/feat-11386.en.md index cb527fa42..740d8f3bf 100644 --- a/changes/ee/feat-11386.en.md +++ b/changes/ee/feat-11386.en.md @@ -1 +1 @@ -Integrated the LDAP as a new authenticator. +Integrated LDAP as a new authenticator. diff --git a/rel/i18n/emqx_ldap_authn.hocon b/rel/i18n/emqx_ldap_authn.hocon index 7c59f2039..04dc88e83 100644 --- a/rel/i18n/emqx_ldap_authn.hocon +++ b/rel/i18n/emqx_ldap_authn.hocon @@ -10,7 +10,7 @@ password_attribute.label: """Password Attribute""" is_superuser_attribute.desc: -"""Indicates which attribute is used to represent whether the user is a super user.""" +"""Indicates which attribute is used to represent whether the user is a superuser.""" is_superuser_attribute.label: """IsSuperuser Attribute""" From 0571fd8cac478a057597d8c233643ca884deb3ee Mon Sep 17 00:00:00 2001 From: firest Date: Fri, 4 Aug 2023 14:51:45 +0800 Subject: [PATCH 2/3] feat(ldap-authz): integrate the LDAP authorization --- apps/emqx_authz/src/emqx_authz.erl | 12 +- apps/emqx_authz/src/emqx_authz_api_schema.erl | 6 +- apps/emqx_authz/src/emqx_authz_enterprise.erl | 66 +++++++ apps/emqx_authz/src/emqx_authz_schema.erl | 14 +- apps/emqx_ldap/rebar.config | 3 +- apps/emqx_ldap/src/emqx_ldap.app.src | 3 +- apps/emqx_ldap/src/emqx_ldap_authz.erl | 164 +++++++++++++++++ apps/emqx_ldap/test/emqx_ldap_authn_SUITE.erl | 1 - apps/emqx_ldap/test/emqx_ldap_authz_SUITE.erl | 173 ++++++++++++++++++ rel/i18n/emqx_ldap_authz.hocon | 27 +++ 10 files changed, 456 insertions(+), 13 deletions(-) create mode 100644 apps/emqx_authz/src/emqx_authz_enterprise.erl create mode 100644 apps/emqx_ldap/src/emqx_ldap_authz.erl create mode 100644 apps/emqx_ldap/test/emqx_ldap_authz_SUITE.erl create mode 100644 rel/i18n/emqx_ldap_authz.hocon diff --git a/apps/emqx_authz/src/emqx_authz.erl b/apps/emqx_authz/src/emqx_authz.erl index 0419bcf72..1398ef8e9 100644 --- a/apps/emqx_authz/src/emqx_authz.erl +++ b/apps/emqx_authz/src/emqx_authz.erl @@ -19,6 +19,8 @@ -behaviour(emqx_config_handler). -behaviour(emqx_config_backup). +-dialyzer({nowarn_function, [authz_module/1]}). + -include("emqx_authz.hrl"). -include_lib("emqx/include/logger.hrl"). -include_lib("emqx/include/emqx_hooks.hrl"). @@ -571,7 +573,12 @@ find_action_in_hooks() -> authz_module(built_in_database) -> emqx_authz_mnesia; 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); @@ -591,8 +598,7 @@ type(built_in_database) -> built_in_database; type(<<"built_in_database">>) -> built_in_database; type(client_info) -> client_info; type(<<"client_info">>) -> client_info; -%% should never happen if the input is type-checked by hocon schema -type(Unknown) -> throw({unknown_authz_source_type, Unknown}). +type(MaybeEnterprise) -> emqx_authz_enterprise:type(MaybeEnterprise). maybe_write_files(#{<<"type">> := <<"file">>} = Source) -> write_acl_file(Source); diff --git a/apps/emqx_authz/src/emqx_authz_api_schema.erl b/apps/emqx_authz/src/emqx_authz_api_schema.erl index 29433e421..7aa34c266 100644 --- a/apps/emqx_authz/src/emqx_authz_api_schema.erl +++ b/apps/emqx_authz/src/emqx_authz_api_schema.erl @@ -95,7 +95,9 @@ fields(position) -> in => body } )} - ]. + ]; +fields(MaybeEnterprise) -> + emqx_authz_enterprise:fields(MaybeEnterprise). %%------------------------------------------------------------------------------ %% http type funcs @@ -283,7 +285,7 @@ authz_sources_types(Type) -> mysql, postgresql, file - ]. + ] ++ emqx_authz_enterprise:authz_sources_types(). to_list(A) when is_atom(A) -> atom_to_list(A); diff --git a/apps/emqx_authz/src/emqx_authz_enterprise.erl b/apps/emqx_authz/src/emqx_authz_enterprise.erl new file mode 100644 index 000000000..7362a003a --- /dev/null +++ b/apps/emqx_authz/src/emqx_authz_enterprise.erl @@ -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. diff --git a/apps/emqx_authz/src/emqx_authz_schema.erl b/apps/emqx_authz/src/emqx_authz_schema.erl index 9e02e8a32..6aa04cdc1 100644 --- a/apps/emqx_authz/src/emqx_authz_schema.erl +++ b/apps/emqx_authz/src/emqx_authz_schema.erl @@ -43,7 +43,8 @@ -export([ headers_no_content_type/1, headers/1, - default_authz/0 + default_authz/0, + authz_common_fields/1 ]). %%-------------------------------------------------------------------- @@ -64,7 +65,8 @@ type_names() -> redis_single, redis_sentinel, redis_cluster - ]. + ] ++ + emqx_authz_enterprise:type_names(). namespace() -> authz. @@ -176,7 +178,9 @@ fields("node_error") -> [ node_name(), {"error", ?HOCON(string(), #{desc => ?DESC("node_error")})} - ]. + ]; +fields(MaybeEnterprise) -> + emqx_authz_enterprise:fields(MaybeEnterprise). common_field() -> [ @@ -220,8 +224,8 @@ desc(redis_sentinel) -> ?DESC(redis_sentinel); desc(redis_cluster) -> ?DESC(redis_cluster); -desc(_) -> - undefined. +desc(MaybeEnterprise) -> + emqx_authz_enterprise:desc(MaybeEnterprise). authz_common_fields(Type) -> [ diff --git a/apps/emqx_ldap/rebar.config b/apps/emqx_ldap/rebar.config index 1e53ccafe..abf0d192f 100644 --- a/apps/emqx_ldap/rebar.config +++ b/apps/emqx_ldap/rebar.config @@ -4,5 +4,6 @@ {deps, [ {emqx_connector, {path, "../../apps/emqx_connector"}}, {emqx_resource, {path, "../../apps/emqx_resource"}}, - {emqx_authn, {path, "../../apps/emqx_authn"}} + {emqx_authn, {path, "../../apps/emqx_authn"}}, + {emqx_authz, {path, "../../apps/emqx_authz"}} ]}. diff --git a/apps/emqx_ldap/src/emqx_ldap.app.src b/apps/emqx_ldap/src/emqx_ldap.app.src index 32ffb4329..bdc9493c7 100644 --- a/apps/emqx_ldap/src/emqx_ldap.app.src +++ b/apps/emqx_ldap/src/emqx_ldap.app.src @@ -5,7 +5,8 @@ {applications, [ kernel, stdlib, - emqx_authn + emqx_authn, + emqx_authz ]}, {env, []}, {modules, []}, diff --git a/apps/emqx_ldap/src/emqx_ldap_authz.erl b/apps/emqx_ldap/src/emqx_ldap_authz.erl new file mode 100644 index 000000000..f5ef38c01 --- /dev/null +++ b/apps/emqx_ldap/src/emqx_ldap_authz.erl @@ -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 + ). diff --git a/apps/emqx_ldap/test/emqx_ldap_authn_SUITE.erl b/apps/emqx_ldap/test/emqx_ldap_authn_SUITE.erl index d984cc89f..fb3b9fc36 100644 --- a/apps/emqx_ldap/test/emqx_ldap_authn_SUITE.erl +++ b/apps/emqx_ldap/test/emqx_ldap_authn_SUITE.erl @@ -1,7 +1,6 @@ %%-------------------------------------------------------------------- %% Copyright (c) 2023 EMQ Technologies Co., Ltd. All Rights Reserved. %%-------------------------------------------------------------------- - -module(emqx_ldap_authn_SUITE). -compile(nowarn_export_all). diff --git a/apps/emqx_ldap/test/emqx_ldap_authz_SUITE.erl b/apps/emqx_ldap/test/emqx_ldap_authz_SUITE.erl new file mode 100644 index 000000000..de037ddf1 --- /dev/null +++ b/apps/emqx_ldap/test/emqx_ldap_authz_SUITE.erl @@ -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. diff --git a/rel/i18n/emqx_ldap_authz.hocon b/rel/i18n/emqx_ldap_authz.hocon new file mode 100644 index 000000000..1ccb085f1 --- /dev/null +++ b/rel/i18n/emqx_ldap_authz.hocon @@ -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""" + +} From b24a9d53439a3fff39cb94f14627deb2300f981f Mon Sep 17 00:00:00 2001 From: firest Date: Fri, 4 Aug 2023 15:05:08 +0800 Subject: [PATCH 3/3] chore(ldap-authz): update apps version && changes --- apps/emqx/src/emqx.app.src | 2 +- apps/emqx_authz/src/emqx_authz.app.src | 2 +- changes/ee/feat-11392.en.md | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 changes/ee/feat-11392.en.md diff --git a/apps/emqx/src/emqx.app.src b/apps/emqx/src/emqx.app.src index cff8cf35b..d9598ee1b 100644 --- a/apps/emqx/src/emqx.app.src +++ b/apps/emqx/src/emqx.app.src @@ -2,7 +2,7 @@ {application, emqx, [ {id, "emqx"}, {description, "EMQX Core"}, - {vsn, "5.1.4"}, + {vsn, "5.1.5"}, {modules, []}, {registered, []}, {applications, [ diff --git a/apps/emqx_authz/src/emqx_authz.app.src b/apps/emqx_authz/src/emqx_authz.app.src index 3311d5983..9de573795 100644 --- a/apps/emqx_authz/src/emqx_authz.app.src +++ b/apps/emqx_authz/src/emqx_authz.app.src @@ -1,7 +1,7 @@ %% -*- mode: erlang -*- {application, emqx_authz, [ {description, "An OTP application"}, - {vsn, "0.1.24"}, + {vsn, "0.1.25"}, {registered, []}, {mod, {emqx_authz_app, []}}, {applications, [ diff --git a/changes/ee/feat-11392.en.md b/changes/ee/feat-11392.en.md new file mode 100644 index 000000000..6ac14abff --- /dev/null +++ b/changes/ee/feat-11392.en.md @@ -0,0 +1 @@ +Integrated LDAP as a authorization source.