dn_lookup_bind option, and rather more tests for the login phase.
This commit is contained in:
parent
815e7cf787
commit
f586ddd335
|
|
@ -46,16 +46,21 @@ check_user_login(Username, []) ->
|
|||
%% Without password, e.g. EXTERNAL
|
||||
?L("CHECK: passwordless login for ~s", [Username]),
|
||||
R = with_ldap(creds(none),
|
||||
fun(LDAP) -> do_login(Username, none, LDAP) end),
|
||||
fun(LDAP) -> do_login(Username, unknown, none, LDAP) end),
|
||||
?L("DECISION: passwordless login for ~s: ~p",
|
||||
[Username, log_result(R)]),
|
||||
R;
|
||||
|
||||
check_user_login(Username, [{password, Password}]) ->
|
||||
?L("CHECK: login for ~s", [Username]),
|
||||
R = with_ldap({ok, {fill_user_dn_pattern(Username), Password}},
|
||||
fun(LDAP) -> do_login(Username, Password, LDAP) end),
|
||||
?L("DECISION: login for ~s: ~p", [Username, log_result(R)]),
|
||||
check_user_login(User, [{password, PW}]) ->
|
||||
?L("CHECK: login for ~s", [User]),
|
||||
R = case dn_lookup_when() of
|
||||
prebind -> UserDN = username_to_dn_prebind(User),
|
||||
with_ldap({ok, {UserDN, PW}},
|
||||
fun(L) -> do_login(User, UserDN, PW, L) end);
|
||||
_ -> with_ldap({ok, {fill_user_dn_pattern(User), PW}},
|
||||
fun(L) -> do_login(User, unknown, PW, L) end)
|
||||
end,
|
||||
?L("DECISION: login for ~s: ~p", [User, log_result(R)]),
|
||||
R;
|
||||
|
||||
check_user_login(Username, AuthProps) ->
|
||||
|
|
@ -287,13 +292,15 @@ env(F) ->
|
|||
{ok, V} = application:get_env(rabbitmq_auth_backend_ldap, F),
|
||||
V.
|
||||
|
||||
do_login(Username, Password, LDAP) ->
|
||||
UserDN = username_to_dn(Username, LDAP),
|
||||
do_login(Username, PrebindUserDN, Password, LDAP) ->
|
||||
UserDN = case PrebindUserDN of
|
||||
unknown -> username_to_dn(Username, LDAP, dn_lookup_when());
|
||||
_ -> PrebindUserDN
|
||||
end,
|
||||
User = #user{username = Username,
|
||||
auth_backend = ?MODULE,
|
||||
impl = #impl{user_dn = UserDN,
|
||||
password = Password}},
|
||||
|
||||
TagRes = [begin
|
||||
?L1("CHECK: does ~s have tag ~s?", [Username, Tag]),
|
||||
R = evaluate(Q, [{username, Username},
|
||||
|
|
@ -307,17 +314,25 @@ do_login(Username, Password, LDAP) ->
|
|||
[E | _] -> E
|
||||
end.
|
||||
|
||||
username_to_dn(Username, LDAP) ->
|
||||
username_to_dn(Username, LDAP, env(dn_lookup_attribute)).
|
||||
dn_lookup_when() -> case {env(dn_lookup_attribute), env(dn_lookup_bind)} of
|
||||
{none, _} -> never;
|
||||
{_, as_user} -> postbind;
|
||||
{_, _} -> prebind
|
||||
end.
|
||||
|
||||
username_to_dn(Username, _LDAP, none) ->
|
||||
fill_user_dn_pattern(Username);
|
||||
username_to_dn_prebind(Username) ->
|
||||
with_ldap({ok, env(dn_lookup_bind)},
|
||||
fun (LDAP) -> dn_lookup(Username, LDAP) end).
|
||||
|
||||
username_to_dn(Username, LDAP, Attr) ->
|
||||
username_to_dn(Username, LDAP, postbind) -> dn_lookup(Username, LDAP);
|
||||
username_to_dn(Username, _LDAP, _When) -> fill_user_dn_pattern(Username).
|
||||
|
||||
dn_lookup(Username, LDAP) ->
|
||||
Filled = fill_user_dn_pattern(Username),
|
||||
case eldap:search(LDAP,
|
||||
[{base, env(dn_lookup_base)},
|
||||
{filter, eldap:equalityMatch(Attr, Filled)},
|
||||
{filter, eldap:equalityMatch(
|
||||
env(dn_lookup_attribute), Filled)},
|
||||
{attributes, ["distinguishedName"]}]) of
|
||||
{ok, #eldap_search_result{entries = [#eldap_entry{object_name = DN}]}}->
|
||||
DN;
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
{user_dn_pattern, "${username}"},
|
||||
{dn_lookup_attribute, none},
|
||||
{dn_lookup_base, none},
|
||||
{dn_lookup_bind, as_user},
|
||||
{other_bind, as_user},
|
||||
{vhost_access_query, {constant, true}},
|
||||
{resource_access_query, {constant, true}},
|
||||
|
|
|
|||
|
|
@ -27,26 +27,78 @@
|
|||
password = <<"password">>,
|
||||
virtual_host = <<"test">>}).
|
||||
|
||||
login_test_() ->
|
||||
[?_test(fail(#amqp_params_network{})),
|
||||
?_test(fail(#amqp_params_network{username = <<"Simon MacMullen">>})),
|
||||
?_test(fail(#amqp_params_network{username = <<"Simon MacMullen">>,
|
||||
password = <<"password">>})),
|
||||
?_test(succ(?SIMON)),
|
||||
?_test(succ(?MIKEB))].
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
succ(Params) -> ?assertMatch({ok, _}, amqp_connection:start(Params)).
|
||||
fail(Params) -> ?assertMatch({error, _}, amqp_connection:start(Params)).
|
||||
login_test_() ->
|
||||
[test_login(Env, L, case {LGood, EnvGood} of
|
||||
{good, good} -> fun succ/1;
|
||||
_ -> fun fail/1
|
||||
end) || {LGood, L} <- logins(),
|
||||
{EnvGood, Env} <- login_envs()].
|
||||
|
||||
logins() ->
|
||||
[{bad, #amqp_params_network{}},
|
||||
{bad, #amqp_params_network{username = <<"Simon MacMullen">>}},
|
||||
{bad, #amqp_params_network{username = <<"Simon MacMullen">>,
|
||||
password = <<"password">>}},
|
||||
{good, ?SIMON},
|
||||
{good, ?MIKEB}].
|
||||
|
||||
login_envs() ->
|
||||
[{good, base_login_env()},
|
||||
{good, dn_lookup_pre_bind_env()},
|
||||
{good, other_bind_admin_env()},
|
||||
{good, other_bind_anon_env()},
|
||||
{bad, other_bind_broken_env()}].
|
||||
|
||||
base_login_env() ->
|
||||
[{user_dn_pattern, "cn=${username},ou=People,dc=example,dc=com"},
|
||||
{dn_lookup_attribute, none},
|
||||
{dn_lookup_base, none},
|
||||
{dn_lookup_bind, as_user},
|
||||
{other_bind, as_user}].
|
||||
|
||||
%% TODO configure OpenLDAP to allow a dn_lookup_post_bind_env()
|
||||
dn_lookup_pre_bind_env() ->
|
||||
[{user_dn_pattern, "${username}"},
|
||||
{dn_lookup_attribute, "cn"},
|
||||
{dn_lookup_base, "OU=People,DC=example,DC=com"},
|
||||
{dn_lookup_bind, {"cn=admin,dc=example,dc=com", "admin"}}].
|
||||
|
||||
other_bind_admin_env() ->
|
||||
[{other_bind, {"cn=admin,dc=example,dc=com", "admin"}}].
|
||||
|
||||
other_bind_anon_env() ->
|
||||
[{other_bind, anon}].
|
||||
|
||||
other_bind_broken_env() ->
|
||||
[{other_bind, {"cn=admin,dc=example,dc=com", "admi"}}].
|
||||
|
||||
test_login(Env, Login, ResultFun) ->
|
||||
?_test(try
|
||||
set_env(Env),
|
||||
ResultFun(Login)
|
||||
after
|
||||
set_env(base_login_env())
|
||||
end).
|
||||
|
||||
set_env(Env) ->
|
||||
[application:set_env(rabbitmq_auth_backend_ldap, K, V) || {K, V} <- Env].
|
||||
|
||||
succ(Login) -> ?assertMatch({ok, _}, amqp_connection:start(Login)).
|
||||
fail(Login) -> ?assertMatch({error, _}, amqp_connection:start(Login)).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
in_group_test_() ->
|
||||
X = [#'exchange.declare'{exchange = <<"test">>}],
|
||||
[test_resource_fun(PTR) || PTR <- [{?SIMON, X, ok},
|
||||
{?MIKEB, X, fail}]].
|
||||
test_resource_funs([{?SIMON, X, ok},
|
||||
{?MIKEB, X, fail}]).
|
||||
|
||||
const_test_() ->
|
||||
Q = [#'queue.declare'{queue = <<"test">>}],
|
||||
[test_resource_fun(PTR) || PTR <- [{?SIMON, Q, ok},
|
||||
{?MIKEB, Q, fail}]].
|
||||
test_resource_funs([{?SIMON, Q, ok},
|
||||
{?MIKEB, Q, fail}]).
|
||||
|
||||
string_match_test_() ->
|
||||
B = fun(N) ->
|
||||
|
|
@ -54,10 +106,9 @@ string_match_test_() ->
|
|||
#'queue.declare'{queue = <<"test">>},
|
||||
#'queue.bind'{exchange = N, queue = <<"test">>}]
|
||||
end,
|
||||
[test_resource_fun(PTR) ||
|
||||
PTR <- [{?SIMON, B(<<"xch-Simon MacMullen-abc123">>), ok},
|
||||
{?SIMON, B(<<"abc123">>), fail},
|
||||
{?SIMON, B(<<"xch-Someone Else-abc123">>), fail}]].
|
||||
test_resource_funs([{?SIMON, B(<<"xch-Simon MacMullen-abc123">>), ok},
|
||||
{?SIMON, B(<<"abc123">>), fail},
|
||||
{?SIMON, B(<<"xch-Someone Else-abc123">>), fail}]).
|
||||
|
||||
boolean_logic_test_() ->
|
||||
Q1 = [#'queue.declare'{queue = <<"test1">>},
|
||||
|
|
@ -69,6 +120,8 @@ boolean_logic_test_() ->
|
|||
{?MIKEB, Q1, fail},
|
||||
{?MIKEB, Q2, fail}]].
|
||||
|
||||
test_resource_funs(PTRs) -> [test_resource_fun(PTR) || PTR <- PTRs].
|
||||
|
||||
test_resource_fun({Person, Things, Result}) ->
|
||||
fun() ->
|
||||
{ok, Conn} = amqp_connection:start(Person),
|
||||
|
|
@ -81,3 +134,5 @@ test_resource_fun({Person, Things, Result}) ->
|
|||
catch exit:_ -> fail
|
||||
end)
|
||||
end.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
|
|
|
|||
Loading…
Reference in New Issue