فهرست منبع

Merge pull request #13864 from lafirest/feat/ldap_memberof

feat(ldap): supported the `memberOf` syntax in query filters
lafirest 1 سال پیش
والد
کامیت
09202ad939

+ 23 - 3
.ci/docker-compose-file/openldap/Dockerfile

@@ -1,8 +1,26 @@
-FROM docker.io/zmstone/openldap:2.5.16@sha256:a813922115a1d1f1b974399595921d1778fae22b3f1ee15dcfa8cfa89700dbc7
+FROM buildpack-deps:bookworm
 
+ARG LDAP_TAG=2.5.16
+
+RUN apt-get update && apt-get install -y groff groff-base ldap-utils
+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 --enable-memberof --enable-refint && make depend && make && make install \
+    && cd .. && rm -rf  openldap-${LDAP_TAG}
+
+## entrypoint
+COPY .ci/docker-compose-file/openldap/entrypoint /usr/local/etc/openldap/entrypoint
+
+## conf && schema
 COPY .ci/docker-compose-file/openldap/slapd.conf /usr/local/etc/openldap/slapd.conf
-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
+
+## data
+COPY apps/emqx_ldap/test/data/emqx.io.ldif /usr/local/etc/openldap/schema/emqx.io.ldif
+COPY apps/emqx_ldap/test/data/emqx.groups.ldif /usr/local/etc/openldap/schema/emqx.groups.ldif
+
+## pem
 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
@@ -14,6 +32,8 @@ WORKDIR /usr/local/etc/openldap
 
 EXPOSE 389 636
 
-ENTRYPOINT ["/usr/local/libexec/slapd", "-h", "ldap:/// ldaps:///", "-d", "3", "-f", "/usr/local/etc/openldap/slapd.conf"]
+#ENTRYPOINT ["/usr/local/libexec/slapd", "-h", "ldap:/// ldaps:///", "-d", "3", "-f", "/usr/local/etc/openldap/slapd.conf"]
+
+ENTRYPOINT ["./entrypoint"]
 
 CMD []

+ 14 - 0
.ci/docker-compose-file/openldap/entrypoint

@@ -0,0 +1,14 @@
+#!/bin/bash
+
+set -e
+
+/usr/local/libexec/slapd -h "ldap:/// ldaps:///" -f /usr/local/etc/openldap/slapd.conf
+
+if [ ! -f ADDED_GROUPS ]; then
+    ldapadd -x -D cn=root,dc=emqx,dc=io -w public -f /usr/local/etc/openldap/schema/emqx.groups.ldif
+    touch ADDED_GROUPS
+fi
+
+PID=$(pgrep -o slapd)
+tail -f --pid="$PID"
+

+ 2 - 1
.ci/docker-compose-file/openldap/slapd.conf

@@ -11,5 +11,6 @@ database mdb
 suffix "dc=emqx,dc=io"
 rootdn "cn=root,dc=emqx,dc=io"
 rootpw {SSHA}eoF7NhNrejVYYyGHqnt+MdKNBh4r1w3W
-
 directory       /usr/local/etc/openldap/data
+overlay memberof
+memberof-refint TRUE

+ 28 - 2
apps/emqx_auth_ldap/test/emqx_authn_ldap_SUITE.erl

@@ -336,16 +336,21 @@ deprecated_raw_ldap_auth_config() ->
     }.
 
 user_seeds() ->
-    New = fun(Username, Password, Result) ->
+    New4 = fun(Username, Password, Result, Params) ->
         #{
             credentials => #{
                 username => Username,
                 password => Password
             },
-            config_params => #{},
+            config_params => Params,
             result => Result
         }
     end,
+
+    New = fun(Username, Password, Result) ->
+        New4(Username, Password, Result, #{})
+    end,
+
     Valid =
         lists:map(
             fun(Idx) ->
@@ -368,6 +373,27 @@ user_seeds() ->
             <<"mqttuser0009 \\\\test\\\\">>,
             <<"mqttuser0009 \\\\test\\\\">>,
             {ok, #{is_superuser => true}}
+        ),
+        %% not in group
+        New4(
+            <<"mqttuser0002">>,
+            <<"mqttuser0002">>,
+            {error, not_authorized},
+            #{<<"filter">> => <<"(memberOf=cn=test,ou=Groups,dc=emqx,dc=io)">>}
+        ),
+        %% in group
+        New4(
+            <<"mqttuser0003">>,
+            <<"mqttuser0003">>,
+            {ok, #{is_superuser => false}},
+            #{<<"filter">> => <<"(memberOf=cn=test,ou=Groups,dc=emqx,dc=io)">>}
+        ),
+        %% non exists group
+        New4(
+            <<"mqttuser0003">>,
+            <<"mqttuser0003">>,
+            {error, not_authorized},
+            #{<<"filter">> => <<"(memberOf=cn=nonexists,ou=Groups,dc=emqx,dc=io)">>}
         )
         | Valid
     ].

+ 1 - 1
apps/emqx_ldap/src/emqx_ldap.app.src

@@ -1,6 +1,6 @@
 {application, emqx_ldap, [
     {description, "EMQX LDAP Connector"},
-    {vsn, "0.1.9"},
+    {vsn, "0.1.10"},
     {registered, []},
     {applications, [
         kernel,

+ 3 - 2
apps/emqx_ldap/src/emqx_ldap_filter_lexer.xrl

@@ -1,8 +1,8 @@
 Definitions.
 
-Control = [()&|!=~><:*]
+Control = [()&|!=~><:*,]
 White = [\s\t\n\r]+
-StringChars = [^()&|!=~><:*\t\n\r\\]
+StringChars = [^()&|!=~><:*,\t\n\r\\]
 Escape = \\\\|\\{Control}|\\{White}
 String = ({Escape}|{StringChars})+
 
@@ -20,6 +20,7 @@ Rules.
 \* : {token, {asterisk, TokenLine}}.
 \:dn : {token, {dn, TokenLine}}.
 \: : {token, {colon, TokenLine}}.
+, : {token, {comma, TokenLine}}.
 {White} : skip_token.
 {String} : {token, {string, TokenLine, to_string(TokenChars)}}.
 %% Leex will hang if a composite operation is missing a character

+ 18 - 2
apps/emqx_ldap/src/emqx_ldap_filter_parser.yrl

@@ -15,10 +15,10 @@ Header "%%--------------------------------------------------------------------
 %%--------------------------------------------------------------------".
 
 Nonterminals
-filter filtercomp filterlist item simple present substring initial any final extensible attr value type dnattrs matchingrule.
+filter filtercomp filterlist item simple present substring initial any final extensible attr value type dnattrs matchingrule dnvalue.
 
 Terminals
-lparen rparen 'and' 'or' 'not' equal approx greaterOrEqual lessOrEqual asterisk colon dn string.
+lparen rparen 'and' 'or' 'not' equal approx greaterOrEqual lessOrEqual asterisk colon dn string comma.
 
 Rootsymbol filter.
 Left 100 present.
@@ -52,6 +52,8 @@ item->
 
 simple ->
     attr equal value: equal('$1', '$3').
+simple ->
+    attr equal dnvalue: equal('$1', '$3').
 simple ->
     attr approx value: approx('$1', '$3').
 simple ->
@@ -100,6 +102,11 @@ attr ->
 value ->
     string: get_value('$1').
 
+dnvalue ->
+    string equal string comma dnvalue: make_dn_value('$1', '$3', '$5').
+dnvalue ->
+    string equal string: make_dn_value('$1', '$3').
+
 type ->
     value: {type, '$1'}.
 
@@ -150,6 +157,15 @@ flatten(List) -> lists:flatten(List).
 get_value({_Token, _Line, Value}) ->
     Value.
 
+make_dn_value(Attr, Value) ->
+    Attr1 = get_value(Attr),
+    Value1 = get_value(Value),
+    Attr1 ++ "=" ++ Value1.
+
+make_dn_value(Attr, Value, Next) ->
+    Prefix = make_dn_value(Attr, Value),
+    Prefix ++ "," ++ Next.
+
 scan_and_parse(Bin) when is_binary(Bin) ->
     scan_and_parse(erlang:binary_to_list(Bin));
 scan_and_parse(String) ->

+ 15 - 0
apps/emqx_ldap/test/data/emqx.groups.ldif

@@ -0,0 +1,15 @@
+
+## groups
+dn: ou=Groups,dc=emqx,dc=io
+objectclass: organizationalUnit
+objectclass: top
+ou: Groups
+
+dn: cn=test,ou=Groups,dc=emqx,dc=io
+cn: test
+objectclass: groupOfNames
+objectclass: top
+member: uid=mqttuser0001,ou=testdevice,dc=emqx,dc=io
+member: uid=mqttuser0003,ou=testdevice,dc=emqx,dc=io
+member: uid=mqttuser0005,ou=testdevice,dc=emqx,dc=io
+

+ 8 - 0
apps/emqx_ldap/test/emqx_ldap_filter_SUITE.erl

@@ -256,6 +256,14 @@ t_escape(_Config) ->
 t_value_eql_dn(_Config) ->
     ?assertEqual('and'([equalityMatch("a", "dn")]), parse("(&(a=dn))")).
 
+t_member_of(_Config) ->
+    ?assertEqual(
+        'and'([
+            equalityMatch("a", "b"), equalityMatch("memberOf", "CN=GroupName,OU=emqx,DC=WL,DC=com")
+        ]),
+        parse("(&(a=b)(memberOf=CN=GroupName,OU=emqx,DC=WL,DC=com))")
+    ).
+
 % %%------------------------------------------------------------------------------
 % %% Helpers
 % %%------------------------------------------------------------------------------

+ 2 - 0
changes/ce/feat-13864.en.md

@@ -0,0 +1,2 @@
+Added support for using `memberOf` syntax in LDAP query filters.
+