Skip to content
Open
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
7 changes: 7 additions & 0 deletions lib/ldap_fluff/active_directory.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,13 @@ def users_from_search_results(search, method)
end
end

# In AD, the relationship between a user account and the "Primary Group" for that account
# is not included in the member and memberof attributes.
if search.respond_to? 'primarygrouptoken'
primary_users = @ldap.search(:base => @ldap.base, :filter => Net::LDAP::Filter.eq('primarygroupid',search['primarygrouptoken'].first))
users += primary_users.map { |user| @member_service.get_login_from_entry(user) }
end

users.flatten.uniq
end

Expand Down
40 changes: 37 additions & 3 deletions lib/ldap_fluff/ad_member_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ def initialize(ldap, config)
super
end

def find_group(gid)
group = @ldap.search(:filter => group_filter(gid), :base => @group_base, :attributes => ['*','primaryGroupToken'])
raise self.class::GIDNotFoundException if (group.nil? || group.empty?)
group
end

# get a list [] of ldap groups for a given user
# in active directory, this means a recursive lookup
def find_user_groups(uid)
Expand All @@ -19,9 +25,20 @@ def find_user_groups(uid)
def _groups_from_ldap_data(payload)
data = []
if !payload.nil?
first_level = payload[:memberof]
total_groups, _ = _walk_group_ancestry(first_level, first_level)
data = (get_groups(first_level + total_groups)).uniq
first_level = payload[:memberof]
normal_groups, _ = _walk_group_ancestry(first_level, first_level)
# In AD, a user's primary group is not included in the memberOf list, and must be handled separately.
# By default, a new user's primary group is 'Domain Users'
primary_groups = []
primary_first_level = []
if !payload[:primarygroupid].nil?
domain_sid = _get_sid_string(payload[:objectsid].first).split('-')[0..-2].join('-')
primary_sid = domain_sid + '-' + payload[:primarygroupid].first
primary_group = @ldap.search(:filter => Net::LDAP::Filter.eq('objectsid', primary_sid), :base => @group_base, :attributes => ['memberof']).first
primary_first_level = primary_group[:dn]
primary_groups, _ = _walk_group_ancestry(primary_first_level, primary_first_level)
end
data = (get_groups(first_level + normal_groups + primary_first_level + primary_groups)).uniq
end
data
end
Expand All @@ -43,6 +60,23 @@ def _walk_group_ancestry(group_dns = [], known_groups = [])
[set, known_groups]
end

def _get_sid_string(sid_bin)
sid = []

# Byte 1: SID structure revision number (always 1 so far...)
sid << sid_bin[0].unpack("H2").first.to_i

# Skip byte 2
# Bytes 3-8: Identifier Authority
sid << sid_bin[2,6].unpack("H*").first.to_i

# Remaining bytes: list of unsigned, 32-bit, little-endian ints
sid += sid_bin.unpack("@8V*")

# Put it all together.
"S-" + sid.join('-')
end

def class_filter
Net::LDAP::Filter.eq("objectclass", "group")
end
Expand Down
25 changes: 23 additions & 2 deletions test/ad_member_services_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def basic_user
end

def basic_group
@ldap.expect(:search, ad_group_payload, [:filter => ad_group_filter("broze"), :base => @config.group_base])
@ldap.expect(:search, ad_group_payload, [:filter => ad_group_filter("broze"), :base => @config.group_base, :attributes=>["*", "primaryGroupToken"]])
end

def nest_deep(n)
Expand Down Expand Up @@ -116,7 +116,7 @@ def test_find_good_group
end

def test_find_missing_group
@ldap.expect(:search, nil, [:filter => ad_group_filter("broze"), :base => @config.group_base])
@ldap.expect(:search, nil, [:filter => ad_group_filter("broze"), :base => @config.group_base, :attributes=>["*", "primaryGroupToken"]])
@adms.ldap = @ldap
assert_raises(LdapFluff::ActiveDirectory::MemberService::GIDNotFoundException) do
@adms.find_group('broze')
Expand Down Expand Up @@ -161,4 +161,25 @@ def test_get_login_from_entry_missing_attr
assert_nil(@adms.get_login_from_entry(entry))
end

def test_find_primary_group
prim_group = Net::LDAP::Entry.new('p_group')
test_user = Net::LDAP::Entry.new('t_user')

prim_group[:cn] = ['p_group']
prim_group[:dn] = ['CN=p_group,DC=corp,DC=example,DC=com']
prim_group[:primarygrouptoken] = ['12345']
prim_group[:member] = []
test_user[:objectsid] = ["\x01\x04\x00\x00\x00\x00\x00\x05\x15\x00\x00\x00\a\x87\x00\x00\xA0[\x00\x00\xE1\x10\x00\x00"]
test_user[:primarygroupid] = ['12345']
test_user[:samaccountname] = ['tuser']
test_user[:memberof] = []

@ldap.expect(:search, [test_user], [:filter => Net::LDAP::Filter.eq('samaccountname','tuser')])
@ldap.expect(:search, [prim_group], [:filter => Net::LDAP::Filter.eq('objectsid', 'S-1-5-21-34567-23456-12345'), :base => @config.group_base, :attributes => ['memberof']])
@ldap.expect(:search, [], [:base => 'CN=p_group,DC=corp,DC=example,DC=com', :scope => Net::LDAP::SearchScope_BaseObject, :attributes => ['memberof']])

@adms.ldap = @ldap
assert_equal(@adms.find_user_groups('tuser'), ['p_group'])
end

end
21 changes: 21 additions & 0 deletions test/ad_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -209,4 +209,25 @@ def test_find_users_with_empty_nested_group
md.verify
end

def test_find_user_from_primary_group
prim_group = Net::LDAP::Entry.new('p_group')
test_user = Net::LDAP::Entry.new('t_user')

prim_group[:cn] = ['p_group']
prim_group[:primarygrouptoken] = ['12345']
prim_group[:member] = []
test_user[:primarygroupid] = ['12345']
test_user[:samaccountname] = ['tuser']

@ldap.expect(:auth, nil, %w(service pass))
@ldap.expect(:bind, true)
2.times { @ldap.expect(:search, [prim_group], [:filter => ad_group_filter('p_group'), :base => @config.group_base, :attributes=>["*", "primaryGroupToken"]]) }
@ldap.expect(:search, [test_user], [:base => @config.base_dn, :filter => Net::LDAP::Filter.eq('primarygroupid','12345')])
@ldap.expect(:base, @config.base_dn)

@ad.ldap = @ldap
@ad.member_service.ldap = @ldap
assert_equal(@ad.users_for_gid('p_group'), ['tuser'])
end

end