Merge branch 'stable'

This commit is contained in:
Daniil Fedotov 2016-04-22 13:41:11 +01:00
commit 231d9ecea2
8 changed files with 430 additions and 103 deletions

View File

@ -8,9 +8,18 @@ cn: wheel
member: cn=Alice,ou=people,dc=example,dc=com
member: cn=Charlie,ou=people,dc=example,dc=com
member: cn=Dominic,ou=people,dc=example,dc=com
member: uid=peter,ou=people,dc=example,dc=com
dn: cn=people,ou=groups,dc=example,dc=com
objectclass: groupOfNames
cn: people
member: cn=Charlie,ou=people,dc=example,dc=com
member: cn=Dominic,ou=people,dc=example,dc=com
member: uid=peter,ou=people,dc=example,dc=com
dn: cn=staff,ou=groups,dc=example,dc=com
objectclass: groupOfNames
cn: people
member: cn=Charlie,ou=people,dc=example,dc=com
member: cn=Dominic,ou=people,dc=example,dc=com
member: uid=peter,ou=people,dc=example,dc=com

View File

@ -0,0 +1,17 @@
dn: cn=module,cn=config
cn: module
objectClass: olcModuleList
olcModuleLoad: memberof
olcModulePath: /usr/lib/ldap
dn: olcOverlay={0}memberof,olcDatabase={1}hdb,cn=config
objectClass: olcConfig
objectClass: olcMemberOf
objectClass: olcOverlayConfig
objectClass: top
olcOverlay: memberof
olcMemberOfDangling: ignore
olcMemberOfRefInt: TRUE
olcMemberOfGroupOC: groupOfNames
olcMemberOfMemberAD: member
olcMemberOfMemberOfAD: memberOf

View File

@ -35,8 +35,53 @@ cn: Charlie
sn: Charlie Boy
userPassword: password
dn: cn=Edward,ou=people,dc=example,dc=com
objectClass: person
cn: Edward
sn: Ed
userPassword: password
dn: cn=John Doe,ou=people,dc=example,dc=com
objectClass: person
cn: John Doe
sn: Doe
userPassword: password
dn: uid=peter,ou=people,dc=example,dc=com
cn: Peter
givenName: Peter
sn: Jones
uid: peter
uidNumber: 5000
gidNumber: 10000
homeDirectory: /home/peter
mail: peter.jones@example.com
objectClass: top
objectClass: posixAccount
objectClass: shadowAccount
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
loginShell: /bin/bash
userPassword: password
memberOf: cn=wheel,ou=groups,dc=example,dc=com
memberOf: cn=staff,ou=groups,dc=example,dc=com
memberOf: cn=people,ou=groups,dc=example,dc=com
dn: uid=carol,ou=people,dc=example,dc=com
cn: Carol
givenName: Carol
sn: Meyers
uid: carol
uidNumber: 655
gidNumber: 10000
homeDirectory: /home/carol
mail: carol.meyers@example.com
objectClass: top
objectClass: posixAccount
objectClass: shadowAccount
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
loginShell: /bin/bash
userPassword: password

View File

@ -0,0 +1,3 @@
dn: cn=module{1},cn=config
add: olcmoduleload
olcmoduleload: refint

View File

@ -0,0 +1,7 @@
dn: olcOverlay={1}refint,olcDatabase={1}hdb,cn=config
objectClass: olcConfig
objectClass: olcOverlayConfig
objectClass: olcRefintConfig
objectClass: top
olcOverlay: {1}refint
olcRefintAttribute: memberof member manager owner

View File

@ -3,6 +3,9 @@
DIR=$(dirname $0)
sudo ldapadd -Y EXTERNAL -H ldapi:/// -f ${DIR}/global.ldif
sudo ldapadd -Q -Y EXTERNAL -H ldapi:/// -f ${DIR}/memberof_init.ldif
sudo ldapmodify -Q -Y EXTERNAL -H ldapi:/// -f ${DIR}/refint_1.ldif
sudo ldapadd -Q -Y EXTERNAL -H ldapi:/// -f ${DIR}/refint_2.ldif
ldapadd -x -D cn=admin,dc=example,dc=com -w admin -f ${DIR}/people.ldif
ldapadd -x -D cn=admin,dc=example,dc=com -w admin -f ${DIR}/groups.ldif
ldapadd -x -D cn=admin,dc=example,dc=com -w admin -f ${DIR}/rabbit.ldif

View File

@ -46,24 +46,27 @@ user_login_authentication(Username, []) ->
[Username, log_result(R)]),
R;
user_login_authentication(Username, [{password, <<>>}]) ->
%% Password "" is special in LDAP, see
%% https://tools.ietf.org/html/rfc4513#section-5.1.2
?L("CHECK: unauthenticated login for ~s", [Username]),
?L("DECISION: unauthenticated login for ~s: denied", [Username]),
{refused, "user '~s' - unauthenticated bind not allowed", [Username]};
user_login_authentication(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;
user_login_authentication(Username, AuthProps) when is_list(AuthProps) ->
case pget(password, AuthProps) of
undefined -> user_login_authentication(Username, []);
<<>> ->
%% Password "" is special in LDAP, see
%% https://tools.ietf.org/html/rfc4513#section-5.1.2
?L("CHECK: unauthenticated login for ~s", [Username]),
?L("DECISION: unauthenticated login for ~s: denied", [Username]),
{refused, "user '~s' - unauthenticated bind not allowed", [Username]};
PW ->
?L("CHECK: login for ~s", [Username]),
R = case dn_lookup_when() of
prebind -> UserDN = username_to_dn_prebind(Username),
with_ldap({ok, {UserDN, PW}},
login_fun(Username, UserDN, PW, AuthProps));
_ -> with_ldap({ok, {fill_user_dn_pattern(Username), PW}},
login_fun(Username, unknown, PW, AuthProps))
end,
?L("DECISION: login for ~s: ~p", [Username, log_result(R)]),
R
end;
user_login_authentication(Username, AuthProps) ->
exit({unknown_auth_props, Username, AuthProps}).
@ -163,23 +166,20 @@ evaluate0({'or', Queries}, Args, User, LDAP) when is_list(Queries) ->
evaluate0({equals, StringQuery1, StringQuery2}, Args, User, LDAP) ->
safe_eval(fun (String1, String2) ->
R = String1 =:= String2,
R = if String1 =:= String2 -> true;
true -> is_multi_attr_member(String1, String2)
end,
?L1("evaluated equals \"~s\", \"~s\": ~s",
[String1, String2, R]),
[format_multi_attr(String1),
format_multi_attr(String2), R]),
R
end,
evaluate(StringQuery1, Args, User, LDAP),
evaluate(StringQuery2, Args, User, LDAP));
evaluate0({match, StringQuery, REQuery}, Args, User, LDAP) ->
safe_eval(fun (String, RE) ->
R = case re:run(String, RE) of
{match, _} -> true;
nomatch -> false
end,
?L1("evaluated match \"~s\" against RE \"~s\": ~s",
[String, RE, R]),
R
safe_eval(fun (String1, String2) ->
do_match(String1, String2)
end,
evaluate(StringQuery, Args, User, LDAP),
evaluate(REQuery, Args, User, LDAP));
@ -196,7 +196,7 @@ evaluate0({attribute, DNPattern, AttributeName}, Args, _User, LDAP) ->
DN = fill(DNPattern, Args),
R = attribute(DN, AttributeName, LDAP),
?L1("evaluated attribute \"~s\" for \"~s\": ~p",
[AttributeName, DN, R]),
[AttributeName, DN, format_multi_attr(R)]),
R;
evaluate0(Q, Args, _User, _LDAP) ->
@ -206,6 +206,32 @@ safe_eval(_F, {error, _}, _) -> false;
safe_eval(_F, _, {error, _}) -> false;
safe_eval(F, V1, V2) -> F(V1, V2).
do_match(S1, S2) ->
case re:run(S1, S2) of
{match, _} -> log_match(S1, S2, R = true),
R;
nomatch ->
%% Do match bidirectionally, if intial RE consists of
%% multi attributes, else log match and return result.
case S2 of
S when length(S) > 1 ->
R = case re:run(S2, S1) of
{match, _} -> true;
nomatch -> false
end,
log_match(S2, S1, R),
R;
_ ->
log_match(S1, S2, R = false),
R
end
end.
log_match(String, RE, Result) ->
?L1("evaluated match \"~s\" against RE \"~s\": ~s",
[format_multi_attr(String),
format_multi_attr(RE), Result]).
object_exists(DN, Filter, LDAP) ->
case eldap:search(LDAP,
[{base, DN},
@ -223,11 +249,8 @@ attribute(DN, AttributeName, LDAP) ->
[{base, DN},
{filter, eldap:present("objectClass")},
{attributes, [AttributeName]}]) of
{ok, #eldap_search_result{entries = [#eldap_entry{attributes = A}]}} ->
case pget(AttributeName, A) of
[Attr] -> Attr;
_ -> {error, not_found}
end;
{ok, #eldap_search_result{entries = E = [#eldap_entry{}|_]}} ->
get_attributes(AttributeName, E);
{ok, #eldap_search_result{entries = _}} ->
{error, not_found};
{error, _} = E ->
@ -323,6 +346,29 @@ get_or_create_conn(IsAnon, Servers, Opts) ->
end
end.
%% Get attribute(s) from eldap entry
get_attributes(_AttrName, []) -> {error, not_found};
get_attributes(AttrName, [#eldap_entry{attributes = A}|Rem]) ->
case pget(AttrName, A) of
[Attr|[]] -> Attr;
Attrs when length(Attrs) > 1 -> Attrs;
_ -> get_attributes(AttrName, Rem)
end;
get_attributes(AttrName, [_|Rem]) -> get_attributes(AttrName, Rem).
%% Format multiple attribute values for logging
format_multi_attr(Attrs) ->
format_multi_attr(io_lib:printable_list(Attrs), Attrs).
format_multi_attr(true, Attrs) -> Attrs;
format_multi_attr(_, Attrs) when is_list(Attrs) -> string:join(Attrs, "; ");
format_multi_attr(_, Error) -> Error.
%% In case of multiple attributes, check for equality bi-directionally
is_multi_attr_member(Str1, Str2) ->
lists:member(Str1, Str2) orelse lists:member(Str2, Str1).
purge_conn(IsAnon, Servers, Opts) ->
Conns = get(ldap_conns),
Key = {IsAnon, Servers, Opts},
@ -372,7 +418,17 @@ env(F) ->
{ok, V} = application:get_env(rabbitmq_auth_backend_ldap, F),
V.
login_fun(User, UserDN, Password, AuthProps) ->
fun(L) -> case pget(vhost, AuthProps) of
undefined -> do_login(User, UserDN, Password, L);
VHost -> do_login(User, UserDN, Password, VHost, L)
end
end.
do_login(Username, PrebindUserDN, Password, LDAP) ->
do_login(Username, PrebindUserDN, Password, <<>>, LDAP).
do_login(Username, PrebindUserDN, Password, VHost, LDAP) ->
UserDN = case PrebindUserDN of
unknown -> username_to_dn(Username, LDAP, dn_lookup_when());
_ -> PrebindUserDN
@ -380,30 +436,31 @@ do_login(Username, PrebindUserDN, Password, LDAP) ->
User = #auth_user{username = Username,
impl = #impl{user_dn = UserDN,
password = Password}},
DTQ = fun (LDAPn) -> do_tag_queries(Username, UserDN, User, LDAPn) end,
DTQ = fun (LDAPn) -> do_tag_queries(Username, UserDN, User, VHost, LDAPn) end,
TagRes = case env(other_bind) of
as_user -> DTQ(LDAP);
_ -> with_ldap(creds(User), DTQ)
end,
case TagRes of
{ok, L} -> case [E || {_, E = {error, _}} <- L] of
[] -> Tags = [Tag || {Tag, true} <- L],
{ok, User#auth_user{tags = Tags}};
[E | _] -> E
end;
{ok, L} -> {ok, User#auth_user{tags = [Tag || {Tag, true} <- L]}};
E -> E
end.
do_tag_queries(Username, UserDN, User, LDAP) ->
do_tag_queries(Username, UserDN, User, VHost, LDAP) ->
{ok, [begin
?L1("CHECK: does ~s have tag ~s?", [Username, Tag]),
R = evaluate(Q, [{username, Username},
{user_dn, UserDN}], User, LDAP),
{user_dn, UserDN} | vhost_if_defined(VHost)],
User, LDAP),
?L1("DECISION: does ~s have tag ~s? ~p",
[Username, Tag, R]),
{Tag, R}
end || {Tag, Q} <- env(tag_queries)]}.
vhost_if_defined([]) -> [];
vhost_if_defined(<<>>) -> [];
vhost_if_defined(VHost) -> [{vhost, VHost}].
dn_lookup_when() -> case {env(dn_lookup_attribute), env(dn_lookup_bind)} of
{none, _} -> never;
{_, as_user} -> postbind;

View File

@ -21,15 +21,26 @@
-define(ALICE_NAME, "Alice").
-define(BOB_NAME, "Bob").
-define(CAROL_NAME, "Carol").
-define(PETER_NAME, "Peter").
-define(VHOST, "test").
-define(ALICE, #amqp_params_network{username = << ?ALICE_NAME >>,
-define(ALICE, #amqp_params_network{username = <<?ALICE_NAME>>,
password = <<"password">>,
virtual_host = << ?VHOST >>}).
virtual_host = <<?VHOST>>}).
-define(BOB, #amqp_params_network{username = << ?BOB_NAME >>,
-define(BOB, #amqp_params_network{username = <<?BOB_NAME>>,
password = <<"password">>,
virtual_host = <<?VHOST>>}).
-define(CAROL, #amqp_params_network{username = <<?CAROL_NAME>>,
password = <<"password">>,
virtual_host = << ?VHOST >>}).
virtual_host = <<?VHOST>>}).
-define(PETER, #amqp_params_network{username = <<?PETER_NAME>>,
password = <<"password">>,
virtual_host = <<?VHOST>>}).
%%--------------------------------------------------------------------
@ -43,7 +54,8 @@ ldap_only_test_() ->
{"LDAP Constant", const()},
{"LDAP String match", string_match()},
{"LDAP Boolean check", boolean_logic()},
{"LDAP Tags", tag_check([monitor])}
{"LDAP Tags", tag_check([monitor])},
{"LDAP Tag Substitution", tag_check_subst()}
]}.
ldap_and_internal_test_() ->
@ -51,16 +63,19 @@ ldap_and_internal_test_() ->
fun () ->
ok = application:set_env(rabbit, auth_backends,
[{rabbit_auth_backend_ldap, rabbit_auth_backend_internal}]),
ok = control_action(add_user, [ ?ALICE_NAME, ""]),
ok = control_action(set_permissions, [ ?ALICE_NAME, "prefix-.*", "prefix-.*", "prefix-.*"]),
ok = control_action(set_user_tags, [ ?ALICE_NAME, "management", "foo"]),
ok = control_action(add_user, [ ?BOB_NAME, ""]),
ok = control_action(set_permissions, [ ?BOB_NAME, "", "", ""])
ok = control_action(add_user, [?ALICE_NAME, ""]),
ok = control_action(set_permissions, [?ALICE_NAME, "prefix-.*", "prefix-.*", "prefix-.*"]),
ok = control_action(set_user_tags, [?ALICE_NAME, "management", "foo"]),
ok = control_action(add_user, [?BOB_NAME, ""]),
ok = control_action(set_permissions, [?BOB_NAME, "", "", ""]),
ok = control_action(add_user, [?PETER_NAME, ""]),
ok = control_action(set_permissions, [?PETER_NAME, "", "", ""])
end,
fun (_) ->
ok = application:unset_env(rabbit, auth_backends),
ok = control_action(delete_user, [ ?ALICE_NAME ]),
ok = control_action(delete_user, [ ?BOB_NAME ])
ok = control_action(delete_user, [?ALICE_NAME]),
ok = control_action(delete_user, [?BOB_NAME]),
ok = control_action(delete_user, [?PETER_NAME])
end,
[ {"LDAP&Internal Login", login()},
{"LDAP&Internal Permissions", permission_match()},
@ -72,63 +87,142 @@ internal_followed_ldap_and_internal_test_() ->
fun () ->
ok = application:set_env(rabbit, auth_backends,
[rabbit_auth_backend_internal, {rabbit_auth_backend_ldap, rabbit_auth_backend_internal}]),
ok = control_action(add_user, [ ?ALICE_NAME, ""]),
ok = control_action(set_permissions, [ ?ALICE_NAME, "prefix-.*", "prefix-.*", "prefix-.*"]),
ok = control_action(set_user_tags, [ ?ALICE_NAME, "management", "foo"]),
ok = control_action(add_user, [ ?BOB_NAME, ""]),
ok = control_action(set_permissions, [ ?BOB_NAME, "", "", ""])
ok = control_action(add_user, [?ALICE_NAME, ""]),
ok = control_action(set_permissions, [?ALICE_NAME, "prefix-.*", "prefix-.*", "prefix-.*"]),
ok = control_action(set_user_tags, [?ALICE_NAME, "management", "foo"]),
ok = control_action(add_user, [?BOB_NAME, ""]),
ok = control_action(set_permissions, [?BOB_NAME, "", "", ""]),
ok = control_action(add_user, [?PETER_NAME, ""]),
ok = control_action(set_permissions, [?PETER_NAME, "", "", ""])
end,
fun (_) ->
ok = application:unset_env(rabbit, auth_backends),
ok = control_action(delete_user, [ ?ALICE_NAME ]),
ok = control_action(delete_user, [ ?BOB_NAME ])
ok = control_action(delete_user, [?ALICE_NAME]),
ok = control_action(delete_user, [?BOB_NAME]),
ok = control_action(delete_user, [?PETER_NAME])
end,
[ {"Internal, LDAP&Internal Login", login()},
{"Internal, LDAP&Internal Permissions", permission_match()},
{"Internal, LDAP&Internal Tags", tag_check([monitor, management, foo])}
]}.
tag_attribution_test_() ->
{setup,
fun () ->
Cfg = case application:get_env(rabbit_auth_backend_ldap, tag_queries) of
undefined -> undefined;
{ok, X} -> X
end,
set_env(tag_query_configuration()),
Cfg
end,
fun (undefined) ->
ok;
(Cfg) ->
ok = application:set_env(rabbit_auth_backend_ldap, tag_queries, Cfg)
end,
{foreachx,
fun (ldap_only) ->
ok = application:set_env(rabbit, auth_backends, [rabbit_auth_backend_ldap]);
(ldap_and_internal) ->
ok = application:set_env(rabbit, auth_backends,
[{rabbit_auth_backend_ldap, rabbit_auth_backend_internal}]),
internal_authorization_setup();
(internal_followed_by_ldap_and_internal) ->
ok = application:set_env(rabbit, auth_backends,
[rabbit_auth_backend_internal,
{rabbit_auth_backend_ldap, rabbit_auth_backend_internal}]),
internal_authorization_setup()
end,
fun (ldap_only, _) ->
ok = application:unset_env(rabbit, auth_backends);
(BackendCfg, _) when BackendCfg == ldap_and_internal;
BackendCfg == internal_followed_by_ldap_and_internal ->
internal_authorization_teardown(),
ok = application:unset_env(rabbit, auth_backends)
end,
[{ldap_only,
fun(_, _) ->
{"LDAP Tag attribution",
tag_check(<<"Edward">>, <<"password">>, [monitor, normal])}
end},
{ldap_and_internal,
fun(_, _) ->
{"LDAP & Internal Tag attribution",
tag_check(<<"Edward">>, <<"password">>,
[monitor, normal] ++ internal_authorization_tags())}
end},
{internal_followed_by_ldap_and_internal,
fun(_, _) ->
{"Internal followed by LDAP & Internal Tag attribution",
tag_check(<<"Edward">>, <<"password">>,
[monitor, normal] ++ internal_authorization_tags())}
end}
]
}
}.
%%--------------------------------------------------------------------
login() ->
[test_login(Env, L, case {LGood, EnvGood} of
{good, good} -> fun succ/1;
_ -> fun fail/1
end) || {LGood, L} <- logins(),
{EnvGood, Env} <- login_envs()].
lists:flatten(
[test_login({N, Env}, L, FilterList, case {LGood, EnvGood} of
{good, good} -> fun succ/1;
_ -> fun fail/1
end) ||
{LGood, FilterList, L, _Tags} <- logins(),
{N, {EnvGood, Env}} <- login_envs()]).
logins() ->
[{bad, #amqp_params_network{}},
{bad, #amqp_params_network{username = << ?ALICE_NAME >>}},
{bad, #amqp_params_network{username = << ?ALICE_NAME >>,
password = <<"password">>}},
{bad, missing_credentials_for_authentication()},
{good, ?ALICE},
{good, ?BOB}].
logins() -> logins_network() ++ logins_direct().
missing_credentials_for_authentication() ->
#amqp_params_network{username = <<"Alice">>,
password = <<"Alicja">>,
virtual_host = << ?VHOST >>}.
%% Format for login tests, {Outcome, FilterList, Login, Tags}.
%% Tests skipped for each login_env reference in FilterList.
logins_network() ->
[{bad, [5, 6], #amqp_params_network{}, []},
{bad, [5, 6], #amqp_params_network{username = <<?ALICE_NAME>>}, []},
{bad, [5, 6], #amqp_params_network{username = <<?ALICE_NAME>>,
password = <<"password">>}, []},
{bad, [5, 6], #amqp_params_network{username = <<"Alice">>,
password = <<"Alicja">>,
virtual_host = <<?VHOST>>}, []},
{bad, [1, 2, 3, 4, 6, 7], ?CAROL, []},
{good, [5, 6], ?ALICE, []},
{good, [5, 6], ?BOB, []},
{good, [1, 2, 3, 4, 6, 7], ?PETER, []}].
logins_direct() ->
[{bad, [5], #amqp_params_direct{}, []},
{bad, [5], #amqp_params_direct{username = <<?ALICE_NAME>>}, []},
{bad, [5], #amqp_params_direct{username = <<?ALICE_NAME>>,
password = <<"password">>}, [management]},
{good, [5], #amqp_params_direct{username = <<?ALICE_NAME>>,
password = <<"password">>,
virtual_host = <<?VHOST>>}, [management]}].
%% Format for login envs, {Reference, {Outcome, Env}}
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()}].
[{1, {good, base_login_env()}},
{2, {good, dn_lookup_pre_bind_env()}},
{3, {good, other_bind_admin_env()}},
{4, {good, other_bind_anon_env()}},
{5, {good, posix_vhost_access_multiattr_env()}},
{6, {good, tag_queries_subst_env()}},
{7, {bad, other_bind_broken_env()}}].
base_login_env() ->
[{user_dn_pattern, "cn=${username},ou=People,dc=example,dc=com"},
[{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}].
{other_bind, as_user},
{tag_queries, [{monitor, {constant, true}},
{administrator, {constant, false}},
{management, {constant, false}}]},
{vhost_access_query, {exists, "ou=${vhost},ou=vhosts,dc=example,dc=com"}}].
%% TODO configure OpenLDAP to allow a dn_lookup_post_bind_env()
dn_lookup_pre_bind_env() ->
[{user_dn_pattern, "${username}"},
[{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"}}].
@ -142,13 +236,52 @@ other_bind_anon_env() ->
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).
tag_queries_subst_env() ->
[{tag_queries, [{administrator, {constant, false}},
{management,
{exists, "ou=${vhost},ou=vhosts,dc=example,dc=com"}}]}].
posix_vhost_access_multiattr_env() ->
[{user_dn_pattern, "uid=${username},ou=People,dc=example,dc=com"},
{vhost_access_query,
{'and', [{exists, "ou=${vhost},ou=vhosts,dc=example,dc=com"},
{equals,
{attribute, "${user_dn}","memberOf"},
{string, "cn=wheel,ou=groups,dc=example,dc=com"}},
{equals,
{attribute, "${user_dn}","memberOf"},
{string, "cn=people,ou=groups,dc=example,dc=com"}},
{equals,
{string, "cn=wheel,ou=groups,dc=example,dc=com"},
{attribute,"${user_dn}","memberOf"}},
{equals,
{string, "cn=people,ou=groups,dc=example,dc=com"},
{attribute, "${user_dn}","memberOf"}},
{match,
{attribute, "${user_dn}","memberOf"},
{string, "cn=wheel,ou=groups,dc=example,dc=com"}},
{match,
{attribute, "${user_dn}","memberOf"},
{string, "cn=people,ou=groups,dc=example,dc=com"}},
{match,
{string, "cn=wheel,ou=groups,dc=example,dc=com"},
{attribute, "${user_dn}","memberOf"}},
{match,
{string, "cn=people,ou=groups,dc=example,dc=com"},
{attribute, "${user_dn}","memberOf"}}
]}}].
test_login({N, Env}, Login, FilterList, ResultFun) ->
case lists:member(N, FilterList) of
true -> [];
_ ->
?_test(try
set_env(Env),
ResultFun(Login)
after
set_env(base_login_env())
end)
end.
set_env(Env) ->
[application:set_env(rabbitmq_auth_backend_ldap, K, V) || {K, V} <- Env].
@ -161,7 +294,7 @@ fail(Login) -> ?assertMatch({error, _}, amqp_connection:start(Login)).
in_group() ->
X = [#'exchange.declare'{exchange = <<"test">>}],
test_resource_funs([{?ALICE, X, ok},
{?BOB, X, fail}]).
{?BOB, X, fail}]).
const() ->
Q = [#'queue.declare'{queue = <<"test">>}],
@ -194,17 +327,70 @@ permission_match() ->
#'queue.declare'{queue = <<"prefix-test">>},
#'queue.bind'{exchange = N, queue = <<"prefix-test">>}]
end,
test_resource_funs([{?ALICE, B(<<"prefix-abc123">>), ok},
{?ALICE, B(<<"abc123">>), fail},
test_resource_funs([{?ALICE, B(<<"prefix-abc123">>), ok},
{?ALICE, B(<<"abc123">>), fail},
{?ALICE, B(<<"xch-Alice-abc123">>), fail}]).
tag_check(Tags) ->
fun() ->
{ok, User} = rabbit_access_control:check_user_pass_login(
<< ?ALICE_NAME >>, <<"password">>),
?assertEqual(Tags, User#user.tags)
end.
%% Tag check tests, with substitution
tag_check_subst() ->
lists:flatten(
[test_tag_check(tag_check(Username, Password, VHost, Outcome, Tags)) ||
{Outcome, _FilterList, #amqp_params_direct{username = Username,
password = Password,
virtual_host = VHost},
Tags} <- logins_direct()]).
%% Tag check
tag_check(Tags) ->
tag_check(<<?ALICE_NAME>>, <<"password">>, Tags).
tag_check(Username, Password, Tags) ->
tag_check(Username, Password, <<>>, good, Tags).
tag_check(Username, Password, VHost, Outcome, Tags)
when is_binary(Username), is_binary(Password), is_binary(VHost), is_list(Tags) ->
fun() ->
{ok, User} = rabbit_access_control:check_user_login(
Username, [{password, Password}, {vhost, VHost}]),
tag_check_outcome(Outcome, Tags, User)
end;
tag_check(_, _, _, _, _) -> fun() -> [] end.
tag_check_outcome(good, Tags, User) -> ?assertEqual(Tags, User#user.tags);
tag_check_outcome(bad, Tags, User) -> ?assertNotEqual(Tags, User#user.tags).
test_tag_check(TagCheckFun) ->
?_test(try
set_env(tag_queries_subst_env()),
TagCheckFun()
after
set_env(base_login_env())
end).
tag_query_configuration() ->
[{tag_queries,
[{administrator, {constant, false}},
%% Query result for tag `management` is FALSE
%% because this object does NOT exist.
{management,
{exists, "cn=${username},ou=Faculty,dc=Computer Science,dc=Engineering"}},
{monitor, {constant, true}},
%% Query result for tag `normal` is TRUE because
%% this object exists.
{normal,
{exists, "cn=${username},ou=people,dc=example,dc=com"}}]}].
internal_authorization_setup() ->
ok = control_action(add_user, ["Edward", ""]),
ok = control_action(set_user_tags, ["Edward"] ++
[ atom_to_list(T) || T <- internal_authorization_tags() ]).
internal_authorization_teardown() ->
ok = control_action(delete_user, ["Edward"]).
internal_authorization_tags() ->
[foo, bar].
%%--------------------------------------------------------------------
@ -249,7 +435,7 @@ default_options() -> [{"-p", ?VHOST}, {"-q", "false"}].
expand_options(As, Bs) ->
lists:foldl(fun({K, _}=A, R) ->
case proplists:is_defined(K, R) of
true -> R;
true -> R;
false -> [A | R]
end
end, Bs, As).