%% vim:ft=erlang:
%% -*- mode: erlang; -*-
%% ----------------------------------------------------------------------------
%% RabbitMQ LDAP Plugin
%%
%% See https://www.rabbitmq.com/ldap.html for details.
%%
%% ----------------------------------------------------------------------------

%%
%% Connecting to the LDAP server(s)
%% ================================
%%

%% Specify servers to bind to. You *must* set this in order for the plugin
%% to work properly.
%%
%% {servers, ["your-server-name-goes-here"]},

{mapping, "auth_ldap.servers", "rabbitmq_auth_backend_ldap.servers",
    [{datatype, {enum, [none]}}]}.

{mapping, "auth_ldap.servers.$server", "rabbitmq_auth_backend_ldap.servers",
    [{datatype, string}]}.

{translation, "rabbitmq_auth_backend_ldap.servers",
fun(Conf) ->
    case cuttlefish:conf_get("auth_ldap.servers", Conf, undefined) of
        none -> [];
        _    ->
            Settings = cuttlefish_variable:filter_by_prefix("auth_ldap.servers", Conf),
            [ V || {_, V} <- Settings ]
    end
end}.

%% Specify the LDAP port to connect to
%%
%% {port, 389},

{mapping, "auth_ldap.port", "rabbitmq_auth_backend_ldap.port",
    [{datatype, integer}]}.

%% LDAP connection/worker pool size
%%
%% {pool_size, 64},

{mapping, "auth_ldap.connection_pool_size", "rabbitmq_auth_backend_ldap.pool_size",
    [{datatype, integer}]}.

%% LDAP connection timeout, in milliseconds or 'infinity'
%%
%% {timeout, infinity},

{mapping, "auth_ldap.timeout", "rabbitmq_auth_backend_ldap.timeout",
    [{datatype, [integer, {atom, infinity}]}]}.

%% LDAP connection inactivity timeout, in milliseconds or 'infinity'
%%
%% {idle_timeout, 300000},

{mapping, "auth_ldap.idle_timeout", "rabbitmq_auth_backend_ldap.idle_timeout",
    [{datatype, [integer, {atom, infinity}]}]}.

%% Enable logging of LDAP queries.
%% One of
%%   - false (no logging is performed)
%%   - true (verbose logging of the logic used by the plugin)
%%   - network (as true, but additionally logs LDAP network traffic)
%%   - network_unsafe (won't try to scrub any credentials)
%%
%% Defaults to false.
%%
%% {log, false},

{mapping, "auth_ldap.log", "rabbitmq_auth_backend_ldap.log",
    [{datatype, {enum, [true, false, network, network_unsafe]}}]}.

%%
%% Authentication
%% ==============
%%

%% Pattern to convert the username given through AMQP to a different
%% form before performing a simple bind
%%
%% {user_bind_pattern, "${ad_user}@${ad_domain}.com"},

{mapping, "auth_ldap.user_bind_pattern", "rabbitmq_auth_backend_ldap.user_bind_pattern",
    [{datatype, string}]}.

%% Pattern to convert the username given through AMQP to a DN before
%% binding
%%
%% {user_dn_pattern, "cn=${username},ou=People,dc=example,dc=com"},

{mapping, "auth_ldap.user_dn_pattern", "rabbitmq_auth_backend_ldap.user_dn_pattern",
    [{datatype, string}]}.

%% Alternatively, you can convert a username to a Distinguished
%% Name via an LDAP lookup after binding. See the documentation for
%% full details.

%% When converting a username to a dn via a lookup, set these to
%% the name of the attribute that represents the user name, and the
%% base DN for the lookup query.
%%
%% {dn_lookup_attribute,   "userPrincipalName"},
%% {dn_lookup_base,        "DC=gopivotal,DC=com"},

{mapping, "auth_ldap.dn_lookup_attribute", "rabbitmq_auth_backend_ldap.dn_lookup_attribute",
    [{datatype, [{enum, [none]}, string]}]}.

{mapping, "auth_ldap.dn_lookup_base", "rabbitmq_auth_backend_ldap.dn_lookup_base",
    [{datatype, [{enum, [none]}, string]}]}.

{mapping, "auth_ldap.dn_lookup_bind", "rabbitmq_auth_backend_ldap.dn_lookup_bind",
    [{datatype, [{enum, [as_user, anon]}]}]}.

{mapping, "auth_ldap.dn_lookup_bind.user_dn", "rabbitmq_auth_backend_ldap.dn_lookup_bind",
    [{datatype, [string]}]}.

{mapping, "auth_ldap.dn_lookup_bind.password", "rabbitmq_auth_backend_ldap.dn_lookup_bind",
    [{datatype, [tagged_binary, binary]}]}.

%%  - as_user (to bind as the authenticated user - requires a password)
%%  - anon    (to bind anonymously)
%%  - {UserDN, Password} (to bind with a specified user name and password)
%%
%% Defaults to 'as_user'.

{translation, "rabbitmq_auth_backend_ldap.dn_lookup_bind",
fun(Conf) ->
    case cuttlefish:conf_get("auth_ldap.dn_lookup_bind", Conf, undefined) of
        as_user -> as_user;
        anon    -> anon;
        _       ->
            User = cuttlefish:conf_get("auth_ldap.dn_lookup_bind.user_dn", Conf),
            Pass = cuttlefish:conf_get("auth_ldap.dn_lookup_bind.password", Conf),
            case {User, Pass} of
                {undefined, _} -> as_user;
                {_, undefined} -> as_user;
                _              -> {User, Pass}
            end
    end
end}.

%% Controls how to bind for authorisation queries and also to
%% retrieve the details of users logging in without presenting a
%% password (e.g., SASL EXTERNAL).
%% One of
%%  - as_user (to bind as the authenticated user - requires a password)
%%  - anon    (to bind anonymously)
%%  - {UserDN, Password} (to bind with a specified user name and password)
%%
%% Defaults to 'as_user'.

{mapping, "auth_ldap.other_bind", "rabbitmq_auth_backend_ldap.other_bind",
    [{datatype, {enum, [as_user, anon]}}]}.

{mapping, "auth_ldap.other_bind.user_dn", "rabbitmq_auth_backend_ldap.other_bind",
    [{datatype, string}]}.

{mapping, "auth_ldap.other_bind.password", "rabbitmq_auth_backend_ldap.other_bind",
    [{datatype, [tagged_binary, binary]}]}.

{translation, "rabbitmq_auth_backend_ldap.other_bind",
fun(Conf) ->
    case cuttlefish:conf_get("auth_ldap.other_bind", Conf, undefined) of
        as_user -> as_user;
        anon    -> anon;
        _       ->
            User = cuttlefish:conf_get("auth_ldap.other_bind.user_dn", Conf),
            Pass = cuttlefish:conf_get("auth_ldap.other_bind.password", Conf),
            case {User, Pass} of
                {undefined, _} -> as_user;
                {_, undefined} -> as_user;
                _              -> {User, Pass}
            end
    end
end}.

%%
%% Authorisation
%% =============
%%

%% Groups are searched in the DN defined by the `group_lookup_base`
%% configuration key, or the `dn_lookup_base` variable if
%% former is `none`.

{mapping, "auth_ldap.group_lookup_base", "rabbitmq_auth_backend_ldap.group_lookup_base",
    [{datatype, [{enum, [none]}, string]}]}.

%% Connect to the LDAP server using TLS
%%
%% {use_ssl, false},

{mapping, "auth_ldap.use_ssl", "rabbitmq_auth_backend_ldap.use_ssl",
    [{datatype, boolean}]}.

%% Connect to the LDAP server using StartTLS
%%
%% {use_starttls, false},

{mapping, "auth_ldap.use_starttls", "rabbitmq_auth_backend_ldap.use_starttls",
    [{datatype, boolean}]}.


%% TLS options

{include_partial, {rabbit, "ssl_options"},
    [{prefix, "auth_ldap.ssl_options"},
     {app_prefix, "rabbitmq_auth_backend_ldap.ssl_options"},
     {disable_with, none}]}.

%% `key.*` is kept inline so the no-match branch stays the atom
%% `undefined`, matching the historical (and buggy) translation shape.

{mapping, "auth_ldap.ssl_options.key.RSAPrivateKey",
    "rabbitmq_auth_backend_ldap.ssl_options.key",
    [{datatype, string}]}.

{mapping, "auth_ldap.ssl_options.key.DSAPrivateKey",
    "rabbitmq_auth_backend_ldap.ssl_options.key",
    [{datatype, string}]}.

{mapping, "auth_ldap.ssl_options.key.PrivateKeyInfo",
    "rabbitmq_auth_backend_ldap.ssl_options.key",
    [{datatype, string}]}.

{translation, "rabbitmq_auth_backend_ldap.ssl_options.key",
fun(Conf) ->
    case cuttlefish_variable:filter_by_prefix(
           "auth_ldap.ssl_options.key", Conf) of
        [{[_,_,Key], Val}|_] -> {list_to_atom(Key), list_to_binary(Val)};
        _ -> undefined
    end
end}.

%% auth_ldap extras on top of the shared partial.

{mapping, "auth_ldap.ssl_options.client_renegotiation",
    "rabbitmq_auth_backend_ldap.ssl_options.client_renegotiation",
    [{datatype, boolean}]}.

{mapping, "auth_ldap.ssl_options.honor_cipher_order",
    "rabbitmq_auth_backend_ldap.ssl_options.honor_cipher_order",
    [{datatype, boolean}]}.

{mapping, "auth_ldap.ssl_options.honor_ecc_order",
    "rabbitmq_auth_backend_ldap.ssl_options.honor_ecc_order",
    [{datatype, boolean}]}.

{mapping, "auth_ldap.ssl_options.sni", "rabbitmq_auth_backend_ldap.ssl_options.server_name_indication",
    [{datatype, [{enum, [none]}, string]}]}.

{translation, "rabbitmq_auth_backend_ldap.ssl_options.server_name_indication",
fun(Conf) ->
    case cuttlefish:conf_get("auth_ldap.ssl_options.sni", Conf, undefined) of
        undefined -> cuttlefish:unset();
        none      -> cuttlefish:unset();
        Hostname  -> Hostname
    end
end}.

{mapping, "auth_ldap.ssl_options.hostname_verification", "rabbitmq_auth_backend_ldap.ssl_hostname_verification", [
    {datatype, {enum, [wildcard, none]}}]}.

%% The LDAP plugin can perform a variety of queries against your
%% LDAP server to determine questions of authorisation. See
%% https://rabbitmq.com/docs/ldap#authorisation for more
%% information.

{mapping, "auth_ldap.queries.vhost_access", "rabbitmq_auth_backend_ldap.vhost_access_query",
    [{datatype, string}]}.

{translation, "rabbitmq_auth_backend_ldap.vhost_access_query",
fun(Conf) ->
    case cuttlefish:conf_get("auth_ldap.queries.vhost_access", Conf, undefined) of
        undefined ->
            cuttlefish:unset();
        Query ->
            rabbit_auth_backend_ldap_util:parse_query(Query)
    end
end}.

{mapping, "auth_ldap.queries.resource_access", "rabbitmq_auth_backend_ldap.resource_access_query",
    [{datatype, string}]}.

{translation, "rabbitmq_auth_backend_ldap.resource_access_query",
fun(Conf) ->
    case cuttlefish:conf_get("auth_ldap.queries.resource_access", Conf, undefined) of
        undefined ->
            cuttlefish:unset();
        Query ->
            rabbit_auth_backend_ldap_util:parse_query(Query)
    end
end}.

{mapping, "auth_ldap.queries.topic_access", "rabbitmq_auth_backend_ldap.topic_access_query",
    [{datatype, string}]}.

{translation, "rabbitmq_auth_backend_ldap.topic_access_query",
fun(Conf) ->
    case cuttlefish:conf_get("auth_ldap.queries.topic_access", Conf, undefined) of
        undefined ->
            cuttlefish:unset();
        Query ->
            rabbit_auth_backend_ldap_util:parse_query(Query)
    end
end}.

{mapping, "auth_ldap.queries.tags", "rabbitmq_auth_backend_ldap.tag_queries",
    [{datatype, string}]}.

{translation, "rabbitmq_auth_backend_ldap.tag_queries",
fun(Conf) ->
    case cuttlefish:conf_get("auth_ldap.queries.tags", Conf, undefined) of
        undefined ->
            cuttlefish:unset();
        Query ->
            rabbit_auth_backend_ldap_util:parse_query(Query)
    end
end}.
