feat(ldap): set test env and add test suites
This commit is contained in:
parent
8c9b136d15
commit
f98f97f37e
|
@ -6,11 +6,11 @@ services:
|
|||
build:
|
||||
context: ../..
|
||||
dockerfile: .ci/docker-compose-file/openldap/Dockerfile
|
||||
args:
|
||||
args:
|
||||
LDAP_TAG: ${LDAP_TAG}
|
||||
image: openldap
|
||||
ports:
|
||||
- 389:389
|
||||
image: openldap
|
||||
#ports:
|
||||
# - 389:389
|
||||
restart: always
|
||||
networks:
|
||||
- emqx_bridge
|
|
@ -1,18 +1,20 @@
|
|||
FROM buildpack-deps:stretch
|
||||
FROM buildpack-deps:bookworm
|
||||
|
||||
ARG LDAP_TAG=2.4.50
|
||||
ARG LDAP_TAG=2.5.16
|
||||
|
||||
RUN apt-get update && apt-get install -y groff groff-base
|
||||
RUN wget ftp://ftp.openldap.org/pub/OpenLDAP/openldap-release/openldap-${LDAP_TAG}.tgz \
|
||||
&& gunzip -c openldap-${LDAP_TAG}.tgz | tar xvfB - \
|
||||
RUN wget https://www.openldap.org/software/download/OpenLDAP/openldap-release/openldap-${LDAP_TAG}.tgz \
|
||||
&& tar xvzf openldap-${LDAP_TAG}.tgz \
|
||||
&& cd openldap-${LDAP_TAG} \
|
||||
&& ./configure && make depend && make && make install \
|
||||
&& cd .. && rm -rf openldap-${LDAP_TAG}
|
||||
|
||||
COPY .ci/docker-compose-file/openldap/slapd.conf /usr/local/etc/openldap/slapd.conf
|
||||
COPY apps/emqx_authn/test/data/emqx.io.ldif /usr/local/etc/openldap/schema/emqx.io.ldif
|
||||
COPY apps/emqx_authn/test/data/emqx.schema /usr/local/etc/openldap/schema/emqx.schema
|
||||
COPY apps/emqx_authn/test/data/certs/*.pem /usr/local/etc/openldap/
|
||||
COPY apps/emqx_ldap/test/data/emqx.io.ldif /usr/local/etc/openldap/schema/emqx.io.ldif
|
||||
COPY apps/emqx_ldap/test/data/emqx.schema /usr/local/etc/openldap/schema/emqx.schema
|
||||
COPY .ci/docker-compose-file/certs/ca.crt /usr/local/etc/openldap/cacert.pem
|
||||
COPY .ci/docker-compose-file/certs/server.crt /usr/local/etc/openldap/cert.pem
|
||||
COPY .ci/docker-compose-file/certs/server.key /usr/local/etc/openldap/key.pem
|
||||
|
||||
RUN mkdir -p /usr/local/etc/openldap/data \
|
||||
&& slapadd -l /usr/local/etc/openldap/schema/emqx.io.ldif -f /usr/local/etc/openldap/slapd.conf
|
||||
|
|
|
@ -1 +1 @@
|
|||
|
||||
ldap
|
||||
|
|
|
@ -58,7 +58,8 @@ fields(config) ->
|
|||
desc => ?DESC(base_object),
|
||||
required => true
|
||||
})},
|
||||
{filter, ?HOCON(binary(), #{desc => ?DESC(filter), default => ""})},
|
||||
{filter,
|
||||
?HOCON(binary(), #{desc => ?DESC(filter), default => <<"(objectClass=mqttUser)">>})},
|
||||
{auto_reconnect, fun ?ECS:auto_reconnect/1}
|
||||
] ++ emqx_connector_schema_lib:ssl_fields().
|
||||
|
||||
|
@ -143,10 +144,10 @@ do_get_status(Conn) ->
|
|||
%% ===================================================================
|
||||
|
||||
connect(Options) ->
|
||||
#{host := Host, username := Username, password := Password} =
|
||||
#{hostname := Host, username := Username, password := Password} =
|
||||
Conf = proplists:get_value(options, Options),
|
||||
OpenOpts = maps:to_list(maps:with([port, sslopts], Conf)),
|
||||
case eldap:open([Host], [{log, fun log/3}, OpenOpts]) of
|
||||
case eldap:open([Host], [{log, fun log/3} | OpenOpts]) of
|
||||
{ok, Handle} = Ret ->
|
||||
case eldap:simple_bind(Handle, Username, Password) of
|
||||
ok -> Ret;
|
||||
|
@ -167,7 +168,7 @@ on_query(
|
|||
[] ->
|
||||
do_ldap_query(InstId, [{base, Base} | SearchOptions], State);
|
||||
_ ->
|
||||
FilterBin = emqx_placeholder:proc_tmpl(FilterTks, Data, #{return => rawlist}),
|
||||
FilterBin = emqx_placeholder:proc_tmpl(FilterTks, Data),
|
||||
case emqx_ldap_filter_parser:scan_and_parse(FilterBin) of
|
||||
{ok, Filter} ->
|
||||
do_ldap_query(
|
||||
|
@ -195,7 +196,7 @@ do_ldap_query(
|
|||
case
|
||||
ecpool:pick_and_do(
|
||||
PoolName,
|
||||
{eldap, search, SearchOptions},
|
||||
{eldap, search, [SearchOptions]},
|
||||
handover
|
||||
)
|
||||
of
|
||||
|
@ -204,14 +205,9 @@ do_ldap_query(
|
|||
ldap_connector_query_return,
|
||||
#{result => Result}
|
||||
),
|
||||
{ok,
|
||||
case Result#eldap_search_result.entries of
|
||||
[First | _] ->
|
||||
%% TODO Support multi entries?
|
||||
First;
|
||||
_ ->
|
||||
undefined
|
||||
end};
|
||||
{ok, Result#eldap_search_result.entries};
|
||||
{error, noSuchObject} ->
|
||||
{ok, []};
|
||||
{error, Reason} ->
|
||||
?SLOG(
|
||||
error,
|
||||
|
@ -235,4 +231,6 @@ prepare_template(Config, State) ->
|
|||
do_prepare_template([{base_object, V} | T], State) ->
|
||||
do_prepare_template(T, State#{base_tokens => emqx_placeholder:preproc_tmpl(V)});
|
||||
do_prepare_template([{filter, V} | T], State) ->
|
||||
do_prepare_template(T, State#{filter_tokens => emqx_placeholder:preproc_tmpl(V)}).
|
||||
do_prepare_template(T, State#{filter_tokens => emqx_placeholder:preproc_tmpl(V)});
|
||||
do_prepare_template([], State) ->
|
||||
State.
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
Definitions.
|
||||
|
||||
Control = [()&|!=~><:*]
|
||||
NonControl = [^()&|!=~><:*]
|
||||
String = {NonControl}*
|
||||
White = [\s\t\n\r]+
|
||||
NonString = [^()&|!=~><:*\s\t\n\r]
|
||||
String = {NonString}+
|
||||
|
||||
Rules.
|
||||
|
||||
|
|
|
@ -16,12 +16,12 @@ filtercomp ->
|
|||
filtercomp ->
|
||||
'or' filterlist: 'or'('$2').
|
||||
filtercomp ->
|
||||
'not' filterlist: 'not'('$2').
|
||||
'not' filter: 'not'('$2').
|
||||
filtercomp ->
|
||||
item: '$1'.
|
||||
|
||||
filterlist ->
|
||||
filter: '$1'.
|
||||
filter: ['$1'].
|
||||
filterlist ->
|
||||
filter filterlist: ['$1' | '$2'].
|
||||
|
||||
|
@ -71,7 +71,7 @@ extensible ->
|
|||
extensible ->
|
||||
type matchingrule colon equal value: extensible('$5', ['$1', '$2']).
|
||||
extensible ->
|
||||
type colon equal value: extensible('$4', []).
|
||||
type colon equal value: extensible('$4', ['$1']).
|
||||
|
||||
extensible ->
|
||||
dnattrs matchingrule colon equal value: extensible('$5', ['$1', '$2']).
|
||||
|
@ -125,7 +125,7 @@ substrings(Attr, List) ->
|
|||
eldap:substrings(Attr, flatten(List)).
|
||||
|
||||
'any'(List, Item) ->
|
||||
[{any, Item} | List].
|
||||
[List, {any, Item}].
|
||||
|
||||
extensible(Value, Opts) -> eldap:extensibleMatch(Value, Opts).
|
||||
|
||||
|
|
|
@ -0,0 +1,135 @@
|
|||
## create emqx.io
|
||||
|
||||
dn:dc=emqx,dc=io
|
||||
objectclass: top
|
||||
objectclass: dcobject
|
||||
objectclass: organization
|
||||
dc:emqx
|
||||
o:emqx,Inc.
|
||||
|
||||
# create testdevice.emqx.io
|
||||
dn:ou=testdevice,dc=emqx,dc=io
|
||||
objectClass: top
|
||||
objectclass:organizationalUnit
|
||||
ou:testdevice
|
||||
|
||||
# create user admin
|
||||
dn:uid=admin,ou=testdevice,dc=emqx,dc=io
|
||||
objectClass: top
|
||||
objectClass: simpleSecurityObject
|
||||
objectClass: account
|
||||
userPassword:: e1NIQX1XNnBoNU1tNVB6OEdnaVVMYlBnekczN21qOWc9
|
||||
uid: admin
|
||||
|
||||
## create user=mqttuser0001,
|
||||
# password=mqttuser0001,
|
||||
# passhash={SHA}mlb3fat40MKBTXUVZwCKmL73R/0=
|
||||
# base64passhash=e1NIQX1tbGIzZmF0NDBNS0JUWFVWWndDS21MNzNSLzA9
|
||||
dn:uid=mqttuser0001,ou=testdevice,dc=emqx,dc=io
|
||||
objectClass: top
|
||||
objectClass: mqttUser
|
||||
objectClass: mqttDevice
|
||||
objectClass: mqttSecurity
|
||||
uid: mqttuser0001
|
||||
isEnabled: TRUE
|
||||
mqttAccountName: user1
|
||||
mqttPublishTopic: mqttuser0001/pub/1
|
||||
mqttPublishTopic: mqttuser0001/pub/+
|
||||
mqttPublishTopic: mqttuser0001/pub/#
|
||||
mqttSubscriptionTopic: mqttuser0001/sub/1
|
||||
mqttSubscriptionTopic: mqttuser0001/sub/+
|
||||
mqttSubscriptionTopic: mqttuser0001/sub/#
|
||||
mqttPubSubTopic: mqttuser0001/pubsub/1
|
||||
mqttPubSubTopic: mqttuser0001/pubsub/+
|
||||
mqttPubSubTopic: mqttuser0001/pubsub/#
|
||||
userPassword:: e1NIQX1tbGIzZmF0NDBNS0JUWFVWWndDS21MNzNSLzA9
|
||||
|
||||
## create user=mqttuser0002
|
||||
# password=mqttuser0002,
|
||||
# passhash={SSHA}n9XdtoG4Q/TQ3TQF4Y+khJbMBH4qXj4M
|
||||
# base64passhash=e1NTSEF9bjlYZHRvRzRRL1RRM1RRRjRZK2toSmJNQkg0cVhqNE0=
|
||||
dn:uid=mqttuser0002,ou=testdevice,dc=emqx,dc=io
|
||||
objectClass: top
|
||||
objectClass: mqttUser
|
||||
objectClass: mqttDevice
|
||||
objectClass: mqttSecurity
|
||||
uid: mqttuser0002
|
||||
isEnabled: TRUE
|
||||
mqttAccountName: user2
|
||||
mqttPublishTopic: mqttuser0002/pub/1
|
||||
mqttPublishTopic: mqttuser0002/pub/+
|
||||
mqttPublishTopic: mqttuser0002/pub/#
|
||||
mqttSubscriptionTopic: mqttuser0002/sub/1
|
||||
mqttSubscriptionTopic: mqttuser0002/sub/+
|
||||
mqttSubscriptionTopic: mqttuser0002/sub/#
|
||||
mqttPubSubTopic: mqttuser0002/pubsub/1
|
||||
mqttPubSubTopic: mqttuser0002/pubsub/+
|
||||
mqttPubSubTopic: mqttuser0002/pubsub/#
|
||||
userPassword:: e1NTSEF9bjlYZHRvRzRRL1RRM1RRRjRZK2toSmJNQkg0cVhqNE0=
|
||||
|
||||
## create user mqttuser0003
|
||||
# password=mqttuser0003,
|
||||
# passhash={MD5}ybsPGoaK3nDyiQvveiCOIw==
|
||||
# base64passhash=e01ENX15YnNQR29hSzNuRHlpUXZ2ZWlDT0l3PT0=
|
||||
dn:uid=mqttuser0003,ou=testdevice,dc=emqx,dc=io
|
||||
objectClass: top
|
||||
objectClass: mqttUser
|
||||
objectClass: mqttDevice
|
||||
objectClass: mqttSecurity
|
||||
uid: mqttuser0003
|
||||
isEnabled: TRUE
|
||||
mqttPublishTopic: mqttuser0003/pub/1
|
||||
mqttPublishTopic: mqttuser0003/pub/+
|
||||
mqttPublishTopic: mqttuser0003/pub/#
|
||||
mqttSubscriptionTopic: mqttuser0003/sub/1
|
||||
mqttSubscriptionTopic: mqttuser0003/sub/+
|
||||
mqttSubscriptionTopic: mqttuser0003/sub/#
|
||||
mqttPubSubTopic: mqttuser0003/pubsub/1
|
||||
mqttPubSubTopic: mqttuser0003/pubsub/+
|
||||
mqttPubSubTopic: mqttuser0003/pubsub/#
|
||||
userPassword:: e01ENX15YnNQR29hSzNuRHlpUXZ2ZWlDT0l3PT0=
|
||||
|
||||
## create user mqttuser0004
|
||||
# password=mqttuser0004,
|
||||
# passhash={MD5}2Br6pPDSEDIEvUlu9+s+MA==
|
||||
# base64passhash=e01ENX0yQnI2cFBEU0VESUV2VWx1OStzK01BPT0=
|
||||
dn:uid=mqttuser0004,ou=testdevice,dc=emqx,dc=io
|
||||
objectClass: top
|
||||
objectClass: mqttUser
|
||||
objectClass: mqttDevice
|
||||
objectClass: mqttSecurity
|
||||
uid: mqttuser0004
|
||||
isEnabled: TRUE
|
||||
mqttPublishTopic: mqttuser0004/pub/1
|
||||
mqttPublishTopic: mqttuser0004/pub/+
|
||||
mqttPublishTopic: mqttuser0004/pub/#
|
||||
mqttSubscriptionTopic: mqttuser0004/sub/1
|
||||
mqttSubscriptionTopic: mqttuser0004/sub/+
|
||||
mqttSubscriptionTopic: mqttuser0004/sub/#
|
||||
mqttPubSubTopic: mqttuser0004/pubsub/1
|
||||
mqttPubSubTopic: mqttuser0004/pubsub/+
|
||||
mqttPubSubTopic: mqttuser0004/pubsub/#
|
||||
userPassword: {MD5}2Br6pPDSEDIEvUlu9+s+MA==
|
||||
|
||||
## create user mqttuser0005
|
||||
# password=mqttuser0005,
|
||||
# passhash={SHA}jKnxeEDGR14kE8AR7yuVFOelhz4=
|
||||
# base64passhash=e1NIQX1qS254ZUVER1IxNGtFOEFSN3l1VkZPZWxoejQ9
|
||||
objectClass: top
|
||||
dn:uid=mqttuser0005,ou=testdevice,dc=emqx,dc=io
|
||||
objectClass: mqttUser
|
||||
objectClass: mqttDevice
|
||||
objectClass: mqttSecurity
|
||||
uid: mqttuser0005
|
||||
isEnabled: TRUE
|
||||
mqttPublishTopic: mqttuser0005/pub/1
|
||||
mqttPublishTopic: mqttuser0005/pub/+
|
||||
mqttPublishTopic: mqttuser0005/pub/#
|
||||
mqttSubscriptionTopic: mqttuser0005/sub/1
|
||||
mqttSubscriptionTopic: mqttuser0005/sub/+
|
||||
mqttSubscriptionTopic: mqttuser0005/sub/#
|
||||
mqttPubSubTopic: mqttuser0005/pubsub/1
|
||||
mqttPubSubTopic: mqttuser0005/pubsub/+
|
||||
mqttPubSubTopic: mqttuser0005/pubsub/#
|
||||
userPassword: {SHA}jKnxeEDGR14kE8AR7yuVFOelhz4=
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
#
|
||||
# Preliminary Apple OS X Native LDAP Schema
|
||||
# This file is subject to change.
|
||||
#
|
||||
attributetype ( 1.3.6.1.4.1.11.2.53.2.2.3.1.2.3.1.3 NAME 'isEnabled'
|
||||
EQUALITY booleanMatch
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.7
|
||||
SINGLE-VALUE
|
||||
USAGE userApplications )
|
||||
|
||||
attributetype ( 1.3.6.1.4.1.11.2.53.2.2.3.1.2.3.4.1 NAME ( 'mqttPublishTopic' 'mpt' )
|
||||
EQUALITY caseIgnoreMatch
|
||||
SUBSTR caseIgnoreSubstringsMatch
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
|
||||
USAGE userApplications )
|
||||
attributetype ( 1.3.6.1.4.1.11.2.53.2.2.3.1.2.3.4.2 NAME ( 'mqttSubscriptionTopic' 'mst' )
|
||||
EQUALITY caseIgnoreMatch
|
||||
SUBSTR caseIgnoreSubstringsMatch
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
|
||||
USAGE userApplications )
|
||||
attributetype ( 1.3.6.1.4.1.11.2.53.2.2.3.1.2.3.4.3 NAME ( 'mqttPubSubTopic' 'mpst' )
|
||||
EQUALITY caseIgnoreMatch
|
||||
SUBSTR caseIgnoreSubstringsMatch
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
|
||||
USAGE userApplications )
|
||||
attributetype ( 1.3.6.1.4.1.11.2.53.2.2.3.1.2.3.4.4 NAME ( 'mqttAccountName' 'man' )
|
||||
EQUALITY caseIgnoreMatch
|
||||
SUBSTR caseIgnoreSubstringsMatch
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
|
||||
USAGE userApplications )
|
||||
|
||||
|
||||
objectclass ( 1.3.6.1.4.1.11.2.53.2.2.3.1.2.3.4 NAME 'mqttUser'
|
||||
AUXILIARY
|
||||
MAY ( mqttPublishTopic $ mqttSubscriptionTopic $ mqttPubSubTopic $ mqttAccountName) )
|
||||
|
||||
objectclass ( 1.3.6.1.4.1.11.2.53.2.2.3.1.2.3.2 NAME 'mqttDevice'
|
||||
SUP top
|
||||
STRUCTURAL
|
||||
MUST ( uid )
|
||||
MAY ( isEnabled ) )
|
||||
|
||||
objectclass ( 1.3.6.1.4.1.11.2.53.2.2.3.1.2.3.3 NAME 'mqttSecurity'
|
||||
SUP top
|
||||
AUXILIARY
|
||||
MAY ( userPassword $ userPKCS12 $ pwdAttribute $ pwdLockout ) )
|
|
@ -22,18 +22,33 @@
|
|||
-include_lib("eunit/include/eunit.hrl").
|
||||
-include_lib("emqx/include/emqx.hrl").
|
||||
-include_lib("stdlib/include/assert.hrl").
|
||||
-include_lib("eldap/include/eldap.hrl").
|
||||
|
||||
-define(MYSQL_HOST, "ldap").
|
||||
-define(MYSQL_RESOURCE_MOD, emqx_ldap).
|
||||
-define(LDAP_HOST, "ldap").
|
||||
-define(LDAP_RESOURCE_MOD, emqx_ldap).
|
||||
|
||||
all() ->
|
||||
emqx_common_test_helpers:all(?MODULE).
|
||||
[
|
||||
{group, tcp},
|
||||
{group, ssl}
|
||||
].
|
||||
|
||||
groups() ->
|
||||
[].
|
||||
Cases = emqx_common_test_helpers:all(?MODULE),
|
||||
[
|
||||
{tcp, Cases},
|
||||
{ssl, Cases}
|
||||
].
|
||||
|
||||
init_per_group(Group, Config) ->
|
||||
[{group, Group} | Config].
|
||||
|
||||
end_per_group(_, Config) ->
|
||||
proplists:delete(group, Config).
|
||||
|
||||
init_per_suite(Config) ->
|
||||
case emqx_common_test_helpers:is_tcp_server_available(?MYSQL_HOST, ?MYSQL_DEFAULT_PORT) of
|
||||
Port = port(tcp),
|
||||
case emqx_common_test_helpers:is_tcp_server_available(?LDAP_HOST, Port) of
|
||||
true ->
|
||||
ok = emqx_common_test_helpers:start_apps([emqx_conf]),
|
||||
ok = emqx_connector_test_helpers:start_apps([emqx_resource]),
|
||||
|
@ -58,22 +73,22 @@ end_per_testcase(_, _Config) ->
|
|||
% %% Testcases
|
||||
% %%------------------------------------------------------------------------------
|
||||
|
||||
t_lifecycle(_Config) ->
|
||||
t_lifecycle(Config) ->
|
||||
perform_lifecycle_check(
|
||||
<<"emqx_ldap_SUITE">>,
|
||||
ldap_config()
|
||||
ldap_config(Config)
|
||||
).
|
||||
|
||||
perform_lifecycle_check(ResourceId, InitialConfig) ->
|
||||
{ok, #{config := CheckedConfig}} =
|
||||
emqx_resource:check_config(?MYSQL_RESOURCE_MOD, InitialConfig),
|
||||
emqx_resource:check_config(?LDAP_RESOURCE_MOD, InitialConfig),
|
||||
{ok, #{
|
||||
state := #{pool_name := PoolName} = State,
|
||||
status := InitialStatus
|
||||
}} = emqx_resource:create_local(
|
||||
ResourceId,
|
||||
?CONNECTOR_RESOURCE_GROUP,
|
||||
?MYSQL_RESOURCE_MOD,
|
||||
?LDAP_RESOURCE_MOD,
|
||||
CheckedConfig,
|
||||
#{}
|
||||
),
|
||||
|
@ -86,15 +101,22 @@ perform_lifecycle_check(ResourceId, InitialConfig) ->
|
|||
emqx_resource:get_instance(ResourceId),
|
||||
?assertEqual({ok, connected}, emqx_resource:health_check(ResourceId)),
|
||||
% % Perform query as further check that the resource is working as expected
|
||||
?assertMatch({ok, _, [[1]]}, emqx_resource:query(ResourceId, test_query_no_params())),
|
||||
?assertMatch({ok, _, [[1]]}, emqx_resource:query(ResourceId, test_query_with_params())),
|
||||
?assertMatch(
|
||||
{ok, _, [[1]]},
|
||||
{ok, [#eldap_entry{attributes = [_, _ | _]}]},
|
||||
emqx_resource:query(ResourceId, test_query_no_attr())
|
||||
),
|
||||
?assertMatch(
|
||||
{ok, [#eldap_entry{attributes = [{"mqttAccountName", _}]}]},
|
||||
emqx_resource:query(ResourceId, test_query_with_attr())
|
||||
),
|
||||
?assertMatch(
|
||||
{ok, _},
|
||||
emqx_resource:query(
|
||||
ResourceId,
|
||||
test_query_with_params_and_timeout()
|
||||
test_query_with_attr_and_timeout()
|
||||
)
|
||||
),
|
||||
?assertMatch({ok, []}, emqx_resource:query(ResourceId, test_query_not_exists())),
|
||||
?assertEqual(ok, emqx_resource:stop(ResourceId)),
|
||||
% Resource will be listed still, but state will be changed and healthcheck will fail
|
||||
% as the worker no longer exists.
|
||||
|
@ -116,13 +138,13 @@ perform_lifecycle_check(ResourceId, InitialConfig) ->
|
|||
{ok, ?CONNECTOR_RESOURCE_GROUP, #{status := InitialStatus}} =
|
||||
emqx_resource:get_instance(ResourceId),
|
||||
?assertEqual({ok, connected}, emqx_resource:health_check(ResourceId)),
|
||||
?assertMatch({ok, _, [[1]]}, emqx_resource:query(ResourceId, test_query_no_params())),
|
||||
?assertMatch({ok, _, [[1]]}, emqx_resource:query(ResourceId, test_query_with_params())),
|
||||
?assertMatch({ok, _}, emqx_resource:query(ResourceId, test_query_no_attr())),
|
||||
?assertMatch({ok, _}, emqx_resource:query(ResourceId, test_query_with_attr())),
|
||||
?assertMatch(
|
||||
{ok, _, [[1]]},
|
||||
{ok, _},
|
||||
emqx_resource:query(
|
||||
ResourceId,
|
||||
test_query_with_params_and_timeout()
|
||||
test_query_with_attr_and_timeout()
|
||||
)
|
||||
),
|
||||
% Stop and remove the resource in one go.
|
||||
|
@ -134,32 +156,51 @@ perform_lifecycle_check(ResourceId, InitialConfig) ->
|
|||
% %%------------------------------------------------------------------------------
|
||||
% %% Helpers
|
||||
% %%------------------------------------------------------------------------------
|
||||
|
||||
ldap_config() ->
|
||||
ldap_config(Config) ->
|
||||
RawConfig = list_to_binary(
|
||||
io_lib:format(
|
||||
""
|
||||
"\n"
|
||||
" auto_reconnect = true\n"
|
||||
" database = mqtt\n"
|
||||
" username= root\n"
|
||||
" username= \"cn=root,dc=emqx,dc=io\"\n"
|
||||
" password = public\n"
|
||||
" pool_size = 8\n"
|
||||
" server = \"~s:~b\"\n"
|
||||
" "
|
||||
" base_object=\"uid=${username},ou=testdevice,dc=emqx,dc=io\"\n"
|
||||
" filter =\"(objectClass=mqttUser)\"\n"
|
||||
" ~ts\n"
|
||||
"",
|
||||
[?MYSQL_HOST, ?MYSQL_DEFAULT_PORT]
|
||||
[?LDAP_HOST, port(Config), ssl(Config)]
|
||||
)
|
||||
),
|
||||
|
||||
{ok, Config} = hocon:binary(RawConfig),
|
||||
#{<<"config">> => Config}.
|
||||
{ok, LDConfig} = hocon:binary(RawConfig),
|
||||
#{<<"config">> => LDConfig}.
|
||||
|
||||
test_query_no_params() ->
|
||||
{sql, <<"SELECT 1">>}.
|
||||
test_query_no_attr() ->
|
||||
{query, data()}.
|
||||
|
||||
test_query_with_params() ->
|
||||
{sql, <<"SELECT ?">>, [1]}.
|
||||
test_query_with_attr() ->
|
||||
{query, data(), ["mqttAccountName"]}.
|
||||
|
||||
test_query_with_params_and_timeout() ->
|
||||
{sql, <<"SELECT ?">>, [1], 1000}.
|
||||
test_query_with_attr_and_timeout() ->
|
||||
{query, data(), ["mqttAccountName"], 5000}.
|
||||
|
||||
test_query_not_exists() ->
|
||||
{query, #{username => <<"not_exists">>}}.
|
||||
|
||||
data() ->
|
||||
#{username => <<"mqttuser0001">>}.
|
||||
|
||||
port(tcp) -> 389;
|
||||
port(ssl) -> 636;
|
||||
port(Config) -> port(proplists:get_value(group, Config)).
|
||||
|
||||
ssl(Config) ->
|
||||
case proplists:get_value(group, Config) of
|
||||
tcp ->
|
||||
"ssl.enable=false";
|
||||
ssl ->
|
||||
"ssl.enable=true\n"
|
||||
"ssl.cacertfile=\"etc/openldap/cacert.pem\""
|
||||
end.
|
||||
|
|
|
@ -0,0 +1,245 @@
|
|||
% %%--------------------------------------------------------------------
|
||||
% %% 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_filter_SUITE).
|
||||
|
||||
-compile(nowarn_export_all).
|
||||
-compile(export_all).
|
||||
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
-include_lib("stdlib/include/assert.hrl").
|
||||
|
||||
-import(eldap, [
|
||||
'and'/1,
|
||||
'or'/1,
|
||||
'not'/1,
|
||||
equalityMatch/2,
|
||||
substrings/2,
|
||||
present/1,
|
||||
greaterOrEqual/2,
|
||||
lessOrEqual/2,
|
||||
approxMatch/2,
|
||||
extensibleMatch/2
|
||||
]).
|
||||
|
||||
all() ->
|
||||
emqx_common_test_helpers:all(?MODULE).
|
||||
|
||||
groups() ->
|
||||
[].
|
||||
|
||||
init_per_suite(Config) ->
|
||||
Config.
|
||||
|
||||
end_per_suite(_Config) ->
|
||||
_ = application:stop(emqx_connector).
|
||||
|
||||
% %%------------------------------------------------------------------------------
|
||||
% %% Testcases
|
||||
% %%------------------------------------------------------------------------------
|
||||
|
||||
t_and(_Config) ->
|
||||
?assertEqual('and'([equalityMatch("a", "1")]), parse("(&(a=1))")),
|
||||
?assertEqual(
|
||||
'and'([equalityMatch("a", "1"), (equalityMatch("b", "2"))]),
|
||||
parse("(&(a=1)(b=2))")
|
||||
),
|
||||
?assertMatch({error, _}, scan_and_parse("(&)")).
|
||||
|
||||
t_or(_Config) ->
|
||||
?assertEqual('or'([equalityMatch("a", "1")]), parse("(|(a=1))")),
|
||||
?assertEqual(
|
||||
'or'([equalityMatch("a", "1"), (equalityMatch("b", "2"))]),
|
||||
parse("(|(a=1)(b=2))")
|
||||
),
|
||||
?assertMatch({error, _}, scan_and_parse("(|)")).
|
||||
|
||||
t_not(_Config) ->
|
||||
?assertEqual('not'(equalityMatch("a", "1")), parse("(!(a=1))")),
|
||||
?assertMatch({error, _}, scan_and_parse("(!)")),
|
||||
?assertMatch({error, _}, scan_and_parse("(!(a=1)(b=1))")).
|
||||
|
||||
t_equalityMatch(_Config) ->
|
||||
?assertEqual(equalityMatch("attr", "value"), parse("(attr=value)")),
|
||||
?assertEqual(equalityMatch("attr", "value"), parse("(attr = value)")),
|
||||
?assertMatch({error, _}, scan_and_parse("(attr=)")),
|
||||
?assertMatch({error, _}, scan_and_parse("(=)")),
|
||||
?assertMatch({error, _}, scan_and_parse("(=value)")).
|
||||
|
||||
t_substrings_initial(_Config) ->
|
||||
?assertEqual(substrings("attr", [{initial, "initial"}]), parse("(attr=initial*)")),
|
||||
?assertEqual(
|
||||
substrings("attr", [{initial, "initial"}, {any, "a"}]),
|
||||
parse("(attr=initial*a*)")
|
||||
),
|
||||
?assertEqual(
|
||||
substrings("attr", [{initial, "initial"}, {any, "a"}, {any, "b"}]),
|
||||
parse("(attr=initial*a*b*)")
|
||||
).
|
||||
|
||||
t_substrings_final(_Config) ->
|
||||
?assertEqual(substrings("attr", [{final, "final"}]), parse("(attr=*final)")),
|
||||
?assertEqual(
|
||||
substrings("attr", [{any, "a"}, {final, "final"}]),
|
||||
parse("(attr=*a*final)")
|
||||
),
|
||||
?assertEqual(
|
||||
substrings("attr", [{any, "a"}, {any, "b"}, {final, "final"}]),
|
||||
parse("(attr=*a*b*final)")
|
||||
).
|
||||
|
||||
t_substrings_initial_final(_Config) ->
|
||||
?assertEqual(
|
||||
substrings("attr", [{initial, "initial"}, {final, "final"}]),
|
||||
parse("(attr=initial*final)")
|
||||
),
|
||||
?assertEqual(
|
||||
substrings("attr", [{initial, "initial"}, {any, "a"}, {final, "final"}]),
|
||||
parse("(attr=initial*a*final)")
|
||||
),
|
||||
?assertEqual(
|
||||
substrings(
|
||||
"attr",
|
||||
[{initial, "initial"}, {any, "a"}, {any, "b"}, {final, "final"}]
|
||||
),
|
||||
parse("(attr=initial*a*b*final)")
|
||||
).
|
||||
|
||||
t_substrings_only_any(_Config) ->
|
||||
?assertEqual(present("attr"), parse("(attr=*)")),
|
||||
?assertEqual(substrings("attr", [{any, "a"}]), parse("(attr=*a*)")),
|
||||
?assertEqual(
|
||||
substrings("attr", [{any, "a"}, {any, "b"}]),
|
||||
parse("(attr=*a*b*)")
|
||||
).
|
||||
|
||||
t_greaterOrEqual(_Config) ->
|
||||
?assertEqual(greaterOrEqual("attr", "value"), parse("(attr>=value)")),
|
||||
?assertEqual(greaterOrEqual("attr", "value"), parse("(attr >= value )")),
|
||||
?assertMatch({error, _}, scan_and_parse("(attr>=)")),
|
||||
?assertMatch({error, _}, scan_and_parse("(>=)")),
|
||||
?assertMatch({error, _}, scan_and_parse("(>=value)")).
|
||||
|
||||
t_lessOrEqual(_Config) ->
|
||||
?assertEqual(lessOrEqual("attr", "value"), parse("(attr<=value)")),
|
||||
?assertEqual(lessOrEqual("attr", "value"), parse("( attr <= value )")),
|
||||
?assertMatch({error, _}, scan_and_parse("(attr<=)")),
|
||||
?assertMatch({error, _}, scan_and_parse("(<=)")),
|
||||
?assertMatch({error, _}, scan_and_parse("(<=value)")).
|
||||
|
||||
t_present(_Config) ->
|
||||
?assertEqual(present("attr"), parse("(attr=*)")),
|
||||
?assertEqual(present("attr"), parse("( attr = * )")).
|
||||
|
||||
t_approxMatch(_Config) ->
|
||||
?assertEqual(approxMatch("attr", "value"), parse("(attr~=value)")),
|
||||
?assertEqual(approxMatch("attr", "value"), parse("( attr ~= value )")),
|
||||
?assertMatch({error, _}, scan_and_parse("(attr~=)")),
|
||||
?assertMatch({error, _}, scan_and_parse("(~=)")),
|
||||
?assertMatch({error, _}, scan_and_parse("(~=value)")).
|
||||
|
||||
t_extensibleMatch_dn(_Config) ->
|
||||
?assertEqual(
|
||||
extensibleMatch("value", [{type, "attr"}, {dnAttributes, true}]), parse("(attr:dn:=value)")
|
||||
),
|
||||
?assertEqual(
|
||||
extensibleMatch("value", [{type, "attr"}, {dnAttributes, true}]),
|
||||
parse("( attr:dn := value )")
|
||||
).
|
||||
|
||||
t_extensibleMatch_rule(_Config) ->
|
||||
?assertEqual(
|
||||
extensibleMatch("value", [{type, "attr"}, {matchingRule, "objectClass"}]),
|
||||
parse("(attr:objectClass:=value)")
|
||||
),
|
||||
?assertEqual(
|
||||
extensibleMatch("value", [{type, "attr"}, {matchingRule, "objectClass"}]),
|
||||
parse("( attr:objectClass := value )")
|
||||
).
|
||||
|
||||
t_extensibleMatch_dn_rule(_Config) ->
|
||||
?assertEqual(
|
||||
extensibleMatch(
|
||||
"value",
|
||||
[
|
||||
{type, "attr"},
|
||||
{dnAttributes, true},
|
||||
{matchingRule, "objectClass"}
|
||||
]
|
||||
),
|
||||
parse("(attr:dn:objectClass:=value)")
|
||||
),
|
||||
?assertEqual(
|
||||
extensibleMatch(
|
||||
"value",
|
||||
[
|
||||
{type, "attr"},
|
||||
{dnAttributes, true},
|
||||
{matchingRule, "objectClass"}
|
||||
]
|
||||
),
|
||||
parse("( attr:dn:objectClass :=value)")
|
||||
).
|
||||
|
||||
t_extensibleMatch_no_dn_rule(_Config) ->
|
||||
?assertEqual(extensibleMatch("value", [{type, "attr"}]), parse("(attr:=value)")),
|
||||
?assertEqual(extensibleMatch("value", [{type, "attr"}]), parse("( attr := value )")).
|
||||
|
||||
t_extensibleMatch_no_type_dn(_Config) ->
|
||||
?assertEqual(
|
||||
extensibleMatch("value", [{matchingRule, "objectClass"}]),
|
||||
parse("(:objectClass:=value)")
|
||||
),
|
||||
?assertEqual(
|
||||
extensibleMatch("value", [{matchingRule, "objectClass"}]),
|
||||
parse("( :objectClass := value )")
|
||||
).
|
||||
|
||||
t_extensibleMatch_no_type_no_dn(_Config) ->
|
||||
?assertEqual(
|
||||
extensibleMatch(
|
||||
"value",
|
||||
[{dnAttributes, true}, {matchingRule, "objectClass"}]
|
||||
),
|
||||
parse("(:dn:objectClass:=value)")
|
||||
),
|
||||
?assertEqual(
|
||||
extensibleMatch(
|
||||
"value",
|
||||
[{dnAttributes, true}, {matchingRule, "objectClass"}]
|
||||
),
|
||||
parse("( :dn:objectClass :=value)")
|
||||
).
|
||||
|
||||
t_extensibleMatch_error(_Config) ->
|
||||
?assertMatch({error, _}, scan_and_parse("(:dn:=value)")),
|
||||
?assertMatch({error, _}, scan_and_parse("(::=value)")),
|
||||
?assertMatch({error, _}, scan_and_parse("(:=)")),
|
||||
?assertMatch({error, _}, scan_and_parse("(attr:=)")).
|
||||
|
||||
t_error(_Config) ->
|
||||
?assertMatch({error, _}, scan_and_parse("(attr=value")),
|
||||
?assertMatch({error, _}, scan_and_parse("attr=value")),
|
||||
?assertMatch({error, _}, scan_and_parse("(a=b)(c=d)")).
|
||||
|
||||
% %%------------------------------------------------------------------------------
|
||||
% %% Helpers
|
||||
% %%------------------------------------------------------------------------------
|
||||
parse(Str) ->
|
||||
{ok, Res} = scan_and_parse(Str),
|
||||
Res.
|
||||
|
||||
scan_and_parse(Str) ->
|
||||
emqx_ldap_filter_parser:scan_and_parse(Str).
|
|
@ -225,6 +225,9 @@ for dep in ${CT_DEPS}; do
|
|||
greptimedb)
|
||||
FILES+=( '.ci/docker-compose-file/docker-compose-greptimedb.yaml' )
|
||||
;;
|
||||
ldap)
|
||||
FILES+=( '.ci/docker-compose-file/docker-compose-ldap.yaml' )
|
||||
;;
|
||||
*)
|
||||
echo "unknown_ct_dependency $dep"
|
||||
exit 1
|
||||
|
|
Loading…
Reference in New Issue