Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.rdoc
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ Your global configuration must provide information about your LDAP host to funct
group_base: # base DN for your LDAP groups, eg ou=Groups,dc=redhat,dc=com
use_netgroups: # false by default, use true if you want to use netgroup triples,
# supported only for server type :free_ipa and :posix
use_rfc4519_group_membership: # false by default, use true if you want to use group membership extensions from RFC4519. Typically you may want to use this if your LDAP server is based on 389DS.
# supported only for server type :posix
server_type: # type of server. default == :posix. :active_directory, :posix, :free_ipa
ad_domain: # domain for your users if using active directory, eg redhat.com
service_user: # service account for authenticating LDAP calls. required unless you enable anon
Expand Down
5 changes: 3 additions & 2 deletions lib/ldap_fluff/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
class LdapFluff::Config
ATTRIBUTES = %w[host port encryption base_dn group_base server_type service_user
service_pass anon_queries attr_login search_filter
instrumentation_service use_netgroups].freeze
instrumentation_service use_netgroups use_rfc4519_group_membership].freeze
ATTRIBUTES.each { |attr| attr_reader attr.to_sym }

DEFAULT_CONFIG = { 'port' => 389,
Expand All @@ -14,7 +14,8 @@ class LdapFluff::Config
'server_type' => :free_ipa,
'anon_queries' => false,
'instrumentation_service' => nil,
'use_netgroups' => false }.freeze
'use_netgroups' => false,
'use_rfc4519_group_membership' => false }.freeze

def initialize(config)
raise ArgumentError unless config.respond_to?(:to_hash)
Expand Down
1 change: 1 addition & 0 deletions lib/ldap_fluff/generic.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ def initialize(config = {})
@base = config.base_dn
@group_base = (config.group_base.empty? ? config.base_dn : config.group_base)
@use_netgroups = config.use_netgroups
@use_rfc4519_group_membership = config.use_rfc4519_group_membership
@member_service = create_member_service(config)
end

Expand Down
12 changes: 8 additions & 4 deletions lib/ldap_fluff/posix.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,14 @@ def users_from_search_results(search, method)
filter = if @use_netgroups
Net::LDAP::Filter.eq('objectClass', 'nisNetgroup')
else
Net::LDAP::Filter.eq('objectClass', 'posixGroup') |
Net::LDAP::Filter.eq('objectClass', 'organizationalunit') |
Net::LDAP::Filter.eq('objectClass', 'groupOfUniqueNames') |
Net::LDAP::Filter.eq('objectClass', 'groupOfNames')
filter = Net::LDAP::Filter.eq('objectClass', 'posixGroup') |
Net::LDAP::Filter.eq('objectClass', 'organizationalunit')
if @use_rfc4519_group_membership
filter = filter |
Net::LDAP::Filter.eq('objectClass', 'groupOfUniqueNames') |
Net::LDAP::Filter.eq('objectClass', 'groupOfNames')
end
filter
end
groups = @ldap.search(:base => search.dn, :filter => filter)
members = groups.map { |group| group.send(method) }.flatten.uniq
Expand Down
17 changes: 16 additions & 1 deletion lib/ldap_fluff/posix_member_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
class LdapFluff::Posix::MemberService < LdapFluff::GenericMemberService
def initialize(ldap, config)
@attr_login = (config.attr_login || 'memberuid')
@use_rfc4519_group_membership = config.use_rfc4519_group_membership
super
end

Expand All @@ -16,8 +17,9 @@ def find_user(uid, base_dn = @base)
# return an ldap user with groups attached
# note : this method is not particularly fast for large ldap systems
def find_user_groups(uid)
user = find_user(uid).first
@ldap.search(
:filter => Net::LDAP::Filter.eq('memberuid', uid),
:filter => user_group_filter(uid, user[:dn].first),
:base => @group_base, :attributes => ["cn"]
).map { |entry| entry[:cn][0] }
end
Expand All @@ -27,4 +29,17 @@ class UIDNotFoundException < LdapFluff::Error

class GIDNotFoundException < LdapFluff::Error
end

private

def user_group_filter(uid, user_dn)
by_member = Net::LDAP::Filter.eq('memberuid', uid)
return by_member unless @use_rfc4519_group_membership

by_name = Net::LDAP::Filter.eq('member', user_dn) &
Net::LDAP::Filter.eq('objectClass', 'groupOfNames')
by_unique_name = Net::LDAP::Filter.eq('uniquemember', user_dn) &
Net::LDAP::Filter.eq('objectClass', 'groupOfUniqueNames')
by_member | by_name | by_unique_name
end
end
2 changes: 1 addition & 1 deletion test/lib/ldap_test_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ def netiq_group_payload
end

def posix_user_payload
[{ :cn => ["john"] }]
[{ :cn => ["john"], :dn => ["cn=john,ou=people,dc=internet,dc=com"] }]
end

def posix_group_payload
Expand Down
4 changes: 1 addition & 3 deletions test/netiq_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -128,9 +128,7 @@ def test_find_users_in_nested_groups
[nested_group],
[{ :base => group.dn,
:filter => Net::LDAP::Filter.eq('objectClass', 'posixGroup') |
Net::LDAP::Filter.eq('objectClass', 'organizationalunit') |
Net::LDAP::Filter.eq('objectClass', 'groupOfUniqueNames') |
Net::LDAP::Filter.eq('objectClass', 'groupOfNames') }])
Net::LDAP::Filter.eq('objectClass', 'organizationalunit') }])
@netiq.ldap = @ldap

md = Minitest::Mock.new
Expand Down
43 changes: 38 additions & 5 deletions test/posix_member_services_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,54 @@ def test_find_user
end

def test_find_user_groups
user = posix_group_payload
@ldap.expect(:search, user, [:filter => @ms.name_filter('john'),
group = posix_group_payload
user = posix_user_payload
username = 'john'

@ldap.expect(:search, user, [:filter => @ms.name_filter(username),
:base => config.base_dn])
filter = Net::LDAP::Filter.eq('memberuid', username)
@ldap.expect(:search, group, [:filter => filter,
:base => config.group_base,
:attributes => ["cn"]])
@ms.ldap = @ldap
assert_equal ['broze'], @ms.find_user_groups('john')
assert_equal ['broze'], @ms.find_user_groups(username)
@ldap.verify
end

def test_find_user_groups_rfc4519
@ms.instance_variable_set('@use_rfc4519_group_membership', true)

group = posix_group_payload
user = posix_user_payload
username = 'john'

@ldap.expect(:search, user, [:filter => @ms.name_filter(username),
:base => config.base_dn])
filter = [Net::LDAP::Filter.eq('memberuid', username),
Net::LDAP::Filter.eq('member', user.first[:dn].first) & Net::LDAP::Filter.eq('objectClass', 'groupOfNames'),
Net::LDAP::Filter.eq('uniquemember', user.first[:dn].first) & Net::LDAP::Filter.eq('objectClass', 'groupOfUniqueNames')
].reduce(&:|)

@ldap.expect(:search, group, [:filter => filter,
:base => config.group_base,
:attributes => ["cn"]])
@ms.ldap = @ldap
assert_equal ['broze'], @ms.find_user_groups(username)
@ldap.verify
end

def test_find_no_groups
@ldap.expect(:search, [], [:filter => @ms.name_filter("john"),
user = posix_user_payload
username = 'john'
@ldap.expect(:search, user, [:filter => @ms.name_filter(username),
:base => config.base_dn])
filter = @ms.send(:user_group_filter, username, user.first[:dn].first)
@ldap.expect(:search, [], [:filter => filter,
:base => config.group_base,
:attributes => ["cn"]])
@ms.ldap = @ldap
assert_equal [], @ms.find_user_groups('john')
assert_equal [], @ms.find_user_groups(username)
@ldap.verify
end

Expand Down
29 changes: 28 additions & 1 deletion test/posix_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -119,13 +119,40 @@ def test_find_users_in_nested_groups
nested_group = Net::LDAP::Entry.new('CN=katellers,CN=foremaners,DC=example,DC=com')
nested_group[:memberuid] = ['testuser']

@ldap.expect(:search,
[nested_group],
[{ :base => group.dn,
:filter => Net::LDAP::Filter.eq('objectClass', 'posixGroup') |
Net::LDAP::Filter.eq('objectClass', 'organizationalunit') }])
@posix.ldap = @ldap

md = Minitest::Mock.new
2.times { md.expect(:find_group, [group], ['foremaners']) }
@posix.member_service = md

assert_equal @posix.users_for_gid('foremaners'), ['testuser']

md.verify
@ldap.verify
end

def test_find_users_in_nested_groups_with_rfc4519
@posix.instance_variable_set('@use_rfc4519_group_membership', true)

service_bind
group = Net::LDAP::Entry.new('CN=foremaners,DC=example,DC=com')
group[:memberuid] = ['katellers']
nested_group = Net::LDAP::Entry.new('CN=katellers,CN=foremaners,DC=example,DC=com')
nested_group[:memberuid] = ['testuser']

@ldap.expect(:search,
[nested_group],
[{ :base => group.dn,
:filter => Net::LDAP::Filter.eq('objectClass', 'posixGroup') |
Net::LDAP::Filter.eq('objectClass', 'organizationalunit') |
Net::LDAP::Filter.eq('objectClass', 'groupOfUniqueNames') |
Net::LDAP::Filter.eq('objectClass', 'groupOfNames')}])
Net::LDAP::Filter.eq('objectClass', 'groupOfNames')
}])
@posix.ldap = @ldap

md = Minitest::Mock.new
Expand Down