Merge branch 'stable' into rabbitmq-auth-backend-ldap-15
This commit is contained in:
commit
8406c0cf77
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -46,3 +46,42 @@ 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
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
dn: cn=module{1},cn=config
|
||||
add: olcmoduleload
|
||||
olcmoduleload: refint
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -163,23 +163,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 +193,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 +203,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 +246,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 +343,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},
|
||||
|
|
|
|||
|
|
@ -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>>}).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
|
|
@ -51,16 +62,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,16 +86,19 @@ 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()},
|
||||
|
|
@ -147,39 +164,45 @@ tag_attribution_test_() ->
|
|||
%%--------------------------------------------------------------------
|
||||
|
||||
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} <- logins(),
|
||||
{N, {EnvGood, Env}} <- login_envs()]).
|
||||
|
||||
%% Format for login tests, {Outcome, FilterList, Login}.
|
||||
%% Tests skipped for each login_env reference in FilterList.
|
||||
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}].
|
||||
|
||||
missing_credentials_for_authentication() ->
|
||||
#amqp_params_network{username = <<"Alice">>,
|
||||
password = <<"Alicja">>,
|
||||
virtual_host = << ?VHOST >>}.
|
||||
[{bad, [5], #amqp_params_network{}},
|
||||
{bad, [5], #amqp_params_network{username = << ?ALICE_NAME >>}},
|
||||
{bad, [5], #amqp_params_network{username = << ?ALICE_NAME >>,
|
||||
password = <<"password">>}},
|
||||
{bad, [5], #amqp_params_network{username = <<"Alice">>,
|
||||
password = <<"Alicja">>,
|
||||
virtual_host = << ?VHOST >>}},
|
||||
{bad, [1, 2, 3, 4, 6], ?CAROL},
|
||||
{good, [5], ?ALICE},
|
||||
{good, [5], ?BOB},
|
||||
{good, [1, 2, 3, 4, 6], ?PETER}].
|
||||
|
||||
%% 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, {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}].
|
||||
{other_bind, as_user},
|
||||
{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() ->
|
||||
|
|
@ -197,13 +220,47 @@ 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).
|
||||
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].
|
||||
|
|
|
|||
Loading…
Reference in New Issue