%%-*- mode: erlang -*- %% emqx_auth_ldap config mapping {mapping, "auth.ldap.servers", "emqx_auth_ldap.ldap", [ {default, "127.0.0.1"}, {datatype, string} ]}. {mapping, "auth.ldap.port", "emqx_auth_ldap.ldap", [ {default, 389}, {datatype, integer} ]}. {mapping, "auth.ldap.pool", "emqx_auth_ldap.ldap", [ {default, 8}, {datatype, integer} ]}. {mapping, "auth.ldap.bind_dn", "emqx_auth_ldap.ldap", [ {datatype, string}, {default, "cn=root,dc=emqx,dc=io"} ]}. {mapping, "auth.ldap.bind_password", "emqx_auth_ldap.ldap", [ {datatype, string}, {default, "public"} ]}. {mapping, "auth.ldap.timeout", "emqx_auth_ldap.ldap", [ {default, "30s"}, {datatype, {duration, ms}} ]}. {mapping, "auth.ldap.ssl", "emqx_auth_ldap.ldap", [ {default, false}, {datatype, {enum, [true, false]}} ]}. {mapping, "auth.ldap.ssl.certfile", "emqx_auth_ldap.ldap", [ {datatype, string} ]}. {mapping, "auth.ldap.ssl.keyfile", "emqx_auth_ldap.ldap", [ {datatype, string} ]}. {mapping, "auth.ldap.ssl.cacertfile", "emqx_auth_ldap.ldap", [ {datatype, string} ]}. {mapping, "auth.ldap.ssl.verify", "emqx_auth_ldap.ldap", [ {default, verify_none}, {datatype, {enum, [verify_none, verify_peer]}} ]}. {mapping, "auth.ldap.ssl.fail_if_no_peer_cert", "emqx_auth_ldap.ldap", [ {datatype, {enum, [true, false]}} ]}. {mapping, "auth.ldap.ssl.server_name_indication", "emqx_auth_ldap.ldap", [ {datatype, string} ]}. {translation, "emqx_auth_ldap.ldap", fun(Conf) -> A2N = fun(A) -> case inet:parse_address(A) of {ok, N} -> N; _ -> A end end, Servers = [A2N(A) || A <- string:tokens(cuttlefish:conf_get("auth.ldap.servers", Conf), ",")], Port = cuttlefish:conf_get("auth.ldap.port", Conf), Pool = cuttlefish:conf_get("auth.ldap.pool", Conf), BindDN = cuttlefish:conf_get("auth.ldap.bind_dn", Conf), BindPassword = cuttlefish:conf_get("auth.ldap.bind_password", Conf), Timeout = cuttlefish:conf_get("auth.ldap.timeout", Conf), Filter = fun(Ls) -> [E || E = {_, V} <- Ls, V /= undefined]end, SslOpts = fun() -> [{certfile, cuttlefish:conf_get("auth.ldap.ssl.certfile", Conf)}, {keyfile, cuttlefish:conf_get("auth.ldap.ssl.keyfile", Conf)}, {cacertfile, cuttlefish:conf_get("auth.ldap.ssl.cacertfile", Conf, undefined)}, {verify, cuttlefish:conf_get("auth.ldap.ssl.verify", Conf, undefined)}, {server_name_indication, cuttlefish:conf_get("auth.ldap.ssl.server_name_indication", Conf, disable)}, {fail_if_no_peer_cert, cuttlefish:conf_get("auth.ldap.ssl.fail_if_no_peer_cert", Conf, undefined)}] end, Opts = [{servers, Servers}, {port, Port}, {timeout, Timeout}, {bind_dn, BindDN}, {bind_password, BindPassword}, {pool, Pool}, {auto_reconnect, 2}], case cuttlefish:conf_get("auth.ldap.ssl", Conf) of true -> [{ssl, true}, {sslopts, Filter(SslOpts())}|Opts]; false -> [{ssl, false}|Opts] end end}. {mapping, "auth.ldap.device_dn", "emqx_auth_ldap.device_dn", [ {default, "ou=device,dc=emqx,dc=io"}, {datatype, string} ]}. {mapping, "auth.ldap.match_objectclass", "emqx_auth_ldap.match_objectclass", [ {default, "mqttUser"}, {datatype, string} ]}. {mapping, "auth.ldap.custom_base_dn", "emqx_auth_ldap.custom_base_dn", [ {default, "${username_attr}=${user},${device_dn}"}, {datatype, string} ]}. %% auth.ldap.filters.1.key = "objectClass" %% auth.ldap.filters.1.value = "mqttUser" %% auth.ldap.filters.1.op = "and" %% auth.ldap.filters.2.key = "uiAttr" %% auth.ldap.filters.2.value "someAttr" %% auth.ldap.filters.2.op = "or" %% auth.ldap.filters.3.key = "someKey" %% auth.ldap.filters.3.value = "someValue" %% The configuratation structure sent to the application: %% [{"objectClass","mqttUser"},"and",{"uiAttr","someAttr"},"or",{"someKey","someAttr"}] %% The resulting LDAP filter would look like this: %% ==> "|(&(objectClass=Class)(uiAttr=someAttr)(someKey=someValue))" {translation, "emqx_auth_ldap.filters", fun(Conf) -> Settings = cuttlefish_variable:filter_by_prefix("auth.ldap.filters", Conf), Keys = [{Num, {key, V}} || {["auth","ldap","filters", Num, "key"], V} <- Settings], Values = [{Num, {value, V}} || {["auth","ldap","filters", Num, "value"], V} <- Settings], Ops = [{Num, {op, V}} || {["auth","ldap","filters", Num, "op"], V} <- Settings], RawFilters = Keys ++ Values ++ Ops, Filters = lists:foldl( fun({Num,{T,V}}, Acc)-> maps:update_with(Num, fun(F)-> maps:put(T,V,F) end, #{T=>V}, Acc) end, #{}, RawFilters), Order=lists:usort(maps:keys(Filters)), lists:reverse( lists:foldl( fun(F,Acc)-> case F of #{key:=K, op:=Op, value:=V} -> [Op,{K,V}|Acc]; #{key:=K, value:=V} -> [{K,V}|Acc] end end, [], lists:map(fun(K) -> maps:get(K, Filters) end, Order))) end}. {mapping, "auth.ldap.filters.$num.key", "emqx_auth_ldap.filters", [ {datatype, string} ]}. {mapping, "auth.ldap.filters.$num.value", "emqx_auth_ldap.filters", [ {datatype, string} ]}. {mapping, "auth.ldap.filters.$num.op", "emqx_auth_ldap.filters", [ {datatype, {enum, [ "or", "and" ] } } ]}. {mapping, "auth.ldap.bind_as_user", "emqx_auth_ldap.bind_as_user", [ {default, false}, {datatype, {enum, [true, false]}} ]}. {mapping, "auth.ldap.username.attributetype", "emqx_auth_ldap.username_attr", [ {default, "uid"}, {datatype, string} ]}. {mapping, "auth.ldap.password.attributetype", "emqx_auth_ldap.password_attr", [ {default, "userPassword"}, {datatype, string} ]}.