ldap_fluff 0.2.5 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of ldap_fluff might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4d1b201862035d1d10565885a1bd42c66bf6a749
4
- data.tar.gz: feb2b3a0f9ce3f6995494ca55d0962e83f12ef99
3
+ metadata.gz: 37c4eced1c338a71de90a9bfd06ad6fcf77f2680
4
+ data.tar.gz: 073cb1a0c739a9a1fc49ff9d32defbd202956504
5
5
  SHA512:
6
- metadata.gz: 6dee98c9c1171ea486d7258cf75e6ff73a88a20a3a83b22edc86b75f406a1853b0f2806b9e90eafbbc18c9b02b610ab5f6202481f241f96520e53efa71c7928a
7
- data.tar.gz: a505593f361c83c8d3687e4601f4063c113b0b8bfbcef63abc699412d11f19b0331bebd54b8d17facefc070a424a1cacc3fec6af6c24042c9fc40f9f0dd73653
6
+ metadata.gz: 3169fd42b66606a9f94761fbec81689d60891d1b6eb7e70a1225fe1305b79342da68422d5a910d838ce8317ece64ecd17787f1617f6d1c602ca64d4597318eb2
7
+ data.tar.gz: cae46d73863511bf0f69b99f0bc839728e89b500cfa6338a6de3fedf5c3fc902d1678f42f2eff378df50dd19de11fc15c9fa43b7d8de47afb82034ffd8525b66
@@ -1,42 +1,22 @@
1
- class LdapFluff::ActiveDirectory
2
- attr_accessor :ldap, :member_service
1
+ class LdapFluff::ActiveDirectory < LdapFluff::Generic
3
2
 
4
3
  def initialize(config = {})
5
- @ldap = Net::LDAP.new(:host => config.host,
6
- :base => config.base_dn,
7
- :port => config.port,
8
- :encryption => config.encryption)
9
- @group_base = config.group_base || config.base_dn
10
- @ad_domain = config.ad_domain
11
4
  @bind_user = config.service_user
12
5
  @bind_pass = config.service_pass
13
6
  @anon = config.anon_queries
14
-
15
- @member_service = MemberService.new(@ldap, @group_base)
7
+ super
16
8
  end
17
9
 
18
10
  def bind?(uid = nil, password = nil)
19
- @ldap.auth("#{uid}@#{@ad_domain}", password)
11
+ @ldap.auth(uid, password)
20
12
  @ldap.bind
21
13
  end
22
14
 
23
- # AD generally does not support un-authenticated searching
24
- # Typically AD admins configure a public user for searching
25
- def service_bind
26
- unless @anon || bind?(@bind_user, @bind_pass)
27
- raise UnauthenticatedActiveDirectoryException, "Could not bind to AD Service User"
28
- end
29
- end
30
-
31
15
  # returns the list of groups to which a user belongs
32
16
  # this query is simpler in active directory
33
17
  def groups_for_uid(uid)
34
18
  service_bind
35
- begin
36
- @member_service.find_user_groups(uid)
37
- rescue MemberService::UIDNotFoundException
38
- return []
39
- end
19
+ super
40
20
  end
41
21
 
42
22
  # active directory stores group membership on a users model
@@ -54,25 +34,34 @@ class LdapFluff::ActiveDirectory
54
34
  end
55
35
 
56
36
  def user_exists?(uid)
57
- begin
58
- service_bind
59
- @member_service.find_user(uid)
60
- rescue MemberService::UIDNotFoundException
61
- return false
62
- end
63
- return true
37
+ service_bind
38
+ super
64
39
  end
65
40
 
66
41
  def group_exists?(gid)
67
- begin
68
- service_bind
69
- @member_service.find_group(gid)
70
- rescue MemberService::GIDNotFoundException
71
- return false
72
- end
73
- return true
42
+ service_bind
43
+ super
74
44
  end
75
45
 
76
- class UnauthenticatedActiveDirectoryException < StandardError
46
+ private
47
+
48
+ def users_from_search_results(search, method)
49
+ users = []
50
+
51
+ search.send(method).each do |member|
52
+ cn = member.downcase.split(',')[0].split('=')[1]
53
+ entry = @member_service.find_user(cn).first
54
+
55
+ objectclasses = entry.objectclass.map(&:downcase)
56
+
57
+ if (%w(organizationalperson person) & objectclasses).present?
58
+ users << @member_service.get_logins([member])
59
+ elsif (%w(organizationalunit group) & objectclasses).present?
60
+ users << users_for_gid(cn)
61
+ end
62
+ end
63
+
64
+ users.flatten.uniq
77
65
  end
66
+
78
67
  end
@@ -1,13 +1,11 @@
1
1
  require 'net/ldap'
2
2
 
3
3
  # Naughty bits of active directory ldap queries
4
- class LdapFluff::ActiveDirectory::MemberService
4
+ class LdapFluff::ActiveDirectory::MemberService < LdapFluff::GenericMemberService
5
5
 
6
- attr_accessor :ldap
7
-
8
- def initialize(ldap, group_base)
9
- @ldap = ldap
10
- @group_base = group_base
6
+ def initialize(ldap, config)
7
+ @attr_login = (config.attr_login || 'samaccountname')
8
+ super
11
9
  end
12
10
 
13
11
  # get a list [] of ldap groups for a given user
@@ -17,23 +15,11 @@ class LdapFluff::ActiveDirectory::MemberService
17
15
  _groups_from_ldap_data(data.first)
18
16
  end
19
17
 
20
- def find_user(uid)
21
- data = @ldap.search(:filter => name_filter(uid))
22
- raise UIDNotFoundException if (data.nil? || data.empty?)
23
- data
24
- end
25
-
26
- def find_group(gid)
27
- data = @ldap.search(:filter => group_filter(gid), :base => @group_base)
28
- raise GIDNotFoundException if (data.nil? || data.empty?)
29
- data
30
- end
31
-
32
18
  # return the :memberof attrs + parents, recursively
33
19
  def _groups_from_ldap_data(payload)
34
20
  data = []
35
21
  if !payload.nil?
36
- first_level = _group_names_from_cn(payload[:memberof])
22
+ first_level = get_groups(payload[:memberof])
37
23
  total_groups = _walk_group_ancestry(first_level)
38
24
  data = (first_level + total_groups).uniq
39
25
  end
@@ -48,42 +34,20 @@ class LdapFluff::ActiveDirectory::MemberService
48
34
  search = @ldap.search(:filter => filter, :base => @group_base)
49
35
  if !search.nil? && !search.first.nil?
50
36
  group = search.first
51
- set += _group_names_from_cn(group[:memberof])
37
+ set += get_groups(group[:memberof])
52
38
  set += _walk_group_ancestry(set)
53
39
  end
54
40
  end
55
41
  set
56
42
  end
57
43
 
58
- def group_filter(gid)
59
- Net::LDAP::Filter.eq("cn", gid)
60
- end
61
-
62
44
  def class_filter
63
45
  Net::LDAP::Filter.eq("objectclass", "group")
64
46
  end
65
47
 
66
- def name_filter(uid)
67
- Net::LDAP::Filter.eq("samaccountname", uid)
68
- end
69
-
70
- # extract the group names from the LDAP style response,
71
- # return string will be something like
72
- # CN=bros,OU=bropeeps,DC=jomara,DC=redhat,DC=com
73
- #
74
- # AD group proc from
75
- # http://erniemiller.org/2008/04/04/simplified-active-directory-authentication/
76
- #
77
- # I think we would normally want to just do the collect at the end,
78
- # but we need the individual names for recursive queries
79
- def _group_names_from_cn(grouplist)
80
- p = proc { |g| g.sub(/.*?CN=(.*?),.*/, '\1') }
81
- grouplist.collect(&p)
82
- end
83
-
84
- class UIDNotFoundException < StandardError
48
+ class UIDNotFoundException < LdapFluff::Error
85
49
  end
86
50
 
87
- class GIDNotFoundException < StandardError
51
+ class GIDNotFoundException < LdapFluff::Error
88
52
  end
89
53
  end
@@ -1,90 +1,85 @@
1
1
  require 'yaml'
2
2
  require 'active_support/core_ext/hash'
3
3
 
4
- class LdapFluff
5
- class ConfigError < StandardError
6
- end
7
-
8
- class Config
9
- ATTRIBUTES = %w[host port encryption base_dn group_base server_type ad_domain service_user
10
- service_pass anon_queries]
11
- ATTRIBUTES.each { |attr| attr_reader attr.to_sym }
12
-
13
- DEFAULT_CONFIG = { 'port' => 389,
14
- 'encryption' => nil,
15
- 'base_dn' => 'dc=company,dc=com',
16
- 'group_base' => 'dc=company,dc=com',
17
- 'server_type' => :free_ipa,
18
- 'ad_domain' => nil,
19
- 'anon_queries' => false }
20
-
21
- def initialize(config)
22
- raise ArgumentError unless config.respond_to?(:to_hash)
23
- config = validate(convert(config))
24
-
25
- ATTRIBUTES.each do |attr|
26
- instance_variable_set(:"@#{attr}", config[attr])
27
- end
4
+ class LdapFluff::Config
5
+ ATTRIBUTES = %w[host port encryption base_dn group_base server_type service_user
6
+ service_pass anon_queries attr_login search_filter]
7
+ ATTRIBUTES.each { |attr| attr_reader attr.to_sym }
8
+
9
+ DEFAULT_CONFIG = { 'port' => 389,
10
+ 'encryption' => nil,
11
+ 'base_dn' => 'dc=company,dc=com',
12
+ 'group_base' => 'dc=company,dc=com',
13
+ 'server_type' => :free_ipa,
14
+ 'anon_queries' => false }
15
+
16
+ def initialize(config)
17
+ raise ArgumentError unless config.respond_to?(:to_hash)
18
+ config = validate(convert(config))
19
+
20
+ ATTRIBUTES.each do |attr|
21
+ instance_variable_set(:"@#{attr}", config[attr])
28
22
  end
23
+ end
29
24
 
30
- private
25
+ private
31
26
 
32
- # @param [#to_hash] config
33
- def convert(config)
34
- config.to_hash.with_indifferent_access.tap do |conf|
35
- %w[encryption server_type].each do |key|
36
- conf[key] = conf[key].to_sym if conf[key]
37
- end
27
+ # @param [#to_hash] config
28
+ def convert(config)
29
+ config.to_hash.with_indifferent_access.tap do |conf|
30
+ %w[encryption server_type].each do |key|
31
+ conf[key] = conf[key].to_sym if conf[key]
38
32
  end
39
33
  end
34
+ end
40
35
 
41
- def missing_keys?(config)
42
- missing_keys = ATTRIBUTES - config.keys
43
- raise ConfigError, "missing configuration for keys: #{missing_keys.join(',')}" unless missing_keys.empty?
44
- end
45
-
46
- def unknown_keys?(config)
47
- unknown_keys = config.keys - ATTRIBUTES
48
- raise ConfigError, "unknown configuration keys: #{unknown_keys.join(',')}" unless unknown_keys.empty?
49
- end
36
+ def missing_keys?(config)
37
+ missing_keys = ATTRIBUTES - config.keys
38
+ raise ConfigError, "missing configuration for keys: #{missing_keys.join(',')}" unless missing_keys.empty?
39
+ end
50
40
 
51
- def all_required_keys?(config)
52
- %w[host port base_dn group_base server_type].all? do |key|
53
- raise ConfigError, "config key #{key} has to be set, it was nil" if config[key].nil?
54
- end
41
+ def unknown_keys?(config)
42
+ unknown_keys = config.keys - ATTRIBUTES
43
+ raise ConfigError, "unknown configuration keys: #{unknown_keys.join(',')}" unless unknown_keys.empty?
44
+ end
55
45
 
56
- %w[service_user service_pass].all? do |key|
57
- if !config['anon_queries'] && config['server_type'] != :posix && config[key].nil?
58
- raise ConfigError, "config key #{key} has to be set, it was nil"
59
- end
60
- end
46
+ def all_required_keys?(config)
47
+ %w[host port base_dn group_base server_type].all? do |key|
48
+ raise ConfigError, "config key #{key} has to be set, it was nil" if config[key].nil?
61
49
  end
62
50
 
63
- def anon_queries_set?(config)
64
- unless [false, true].include?(config['anon_queries'])
65
- raise ConfigError, "config key anon_queries has to be true or false but was #{config['anon_queries']}"
51
+ %w[service_user service_pass].all? do |key|
52
+ if !config['anon_queries'] && config['server_type'] != :posix && config[key].nil?
53
+ raise ConfigError, "config key #{key} has to be set, it was nil"
66
54
  end
67
55
  end
56
+ end
68
57
 
69
- def correct_server_type?(config)
70
- unless [:posix, :active_directory, :free_ipa].include?(config['server_type'])
71
- raise ConfigError, 'config key server_type has to be :active_directory, :posix, :free_ipa ' +
72
- "but was #{config['server_type']}"
73
- end
58
+ def anon_queries_set?(config)
59
+ unless [false, true].include?(config['anon_queries'])
60
+ raise ConfigError, "config key anon_queries has to be true or false but was #{config['anon_queries']}"
74
61
  end
62
+ end
75
63
 
76
- def validate(config)
77
- config = DEFAULT_CONFIG.merge(config)
64
+ def correct_server_type?(config)
65
+ unless [:posix, :active_directory, :free_ipa].include?(config['server_type'])
66
+ raise ConfigError, 'config key server_type has to be :active_directory, :posix, :free_ipa ' +
67
+ "but was #{config['server_type']}"
68
+ end
69
+ end
78
70
 
79
- correct_server_type?(config)
80
- missing_keys?(config)
81
- unknown_keys?(config)
82
- all_required_keys?(config)
83
- anon_queries_set?(config)
71
+ def validate(config)
72
+ config = DEFAULT_CONFIG.merge(config)
84
73
 
85
- config
86
- end
74
+ correct_server_type?(config)
75
+ missing_keys?(config)
76
+ unknown_keys?(config)
77
+ all_required_keys?(config)
78
+ anon_queries_set?(config)
87
79
 
88
- end # Config
80
+ config
81
+ end
89
82
 
90
- end # LdapFluff
83
+ class ConfigError < LdapFluff::Error
84
+ end
85
+ end
@@ -0,0 +1,5 @@
1
+ class LdapFluff
2
+ class Error < StandardError
3
+ end
4
+ end
5
+
@@ -1,19 +1,11 @@
1
- class LdapFluff::FreeIPA
2
-
3
- attr_accessor :ldap, :member_service
1
+ class LdapFluff::FreeIPA < LdapFluff::Generic
4
2
 
5
3
  def initialize(config = {})
6
- @ldap = Net::LDAP.new(:host => config.host,
7
- :base => config.base_dn,
8
- :port => config.port,
9
- :encryption => config.encryption)
10
- @group_base = config.group_base || config.base_dn
11
4
  @base = config.base_dn
12
5
  @bind_user = config.service_user
13
6
  @bind_pass = config.service_pass
14
7
  @anon = config.anon_queries
15
-
16
- @member_service = MemberService.new(@ldap, @group_base)
8
+ super
17
9
  end
18
10
 
19
11
  def bind?(uid = nil, password = nil)
@@ -22,21 +14,11 @@ class LdapFluff::FreeIPA
22
14
  end
23
15
 
24
16
  def groups_for_uid(uid)
25
- service_bind
26
17
  begin
27
- @member_service.find_user_groups(uid)
28
- rescue MemberService::UIDNotFoundException
29
- return []
18
+ service_bind
19
+ super
30
20
  rescue MemberService::InsufficientQueryPrivilegesException
31
- raise UnauthenticatedFreeIPAException, "Insufficient Privileges to query groups data"
32
- end
33
- end
34
-
35
- # AD generally does not support un-authenticated searching
36
- # Typically AD admins configure a public user for searching
37
- def service_bind
38
- unless @anon || bind?(@bind_user, @bind_pass)
39
- raise UnauthenticatedFreeIPAException, "Could not bind to FreeIPA Query User"
21
+ raise UnauthenticatedException, "Insufficient Privileges to query groups data"
40
22
  end
41
23
  end
42
24
 
@@ -58,26 +40,30 @@ class LdapFluff::FreeIPA
58
40
  end
59
41
 
60
42
  def user_exists?(uid)
61
- begin
62
- service_bind
63
- @member_service.find_user(uid)
64
- rescue MemberService::UIDNotFoundException
65
- return false
66
- end
67
- return true
43
+ service_bind
44
+ super
68
45
  end
69
46
 
70
47
  def group_exists?(gid)
71
- begin
72
- service_bind
73
- @member_service.find_group(gid)
74
- rescue MemberService::GIDNotFoundException
75
- return false
76
- end
77
- return true
48
+ service_bind
49
+ super
78
50
  end
79
51
 
80
- class UnauthenticatedFreeIPAException < StandardError
81
- end
52
+ private
82
53
 
54
+ def users_from_search_results(search, method)
55
+ # Member results come in the form uid=sampleuser,cn=users, etc.. or gid=samplegroup,cn=groups
56
+ users = []
57
+
58
+ search.send(method).each do |member|
59
+ type = member.downcase.split(',')[1]
60
+ if type == 'cn=users'
61
+ users << @member_service.get_logins([member])
62
+ elsif type == 'cn=groups'
63
+ users << users_for_gid(member.split(',')[0].split('=')[1])
64
+ end
65
+ end
66
+
67
+ users.flatten.uniq
68
+ end
83
69
  end
@@ -1,13 +1,11 @@
1
1
  require 'net/ldap'
2
2
 
3
3
  # handles the naughty bits of posix ldap
4
- class LdapFluff::FreeIPA::MemberService
4
+ class LdapFluff::FreeIPA::MemberService < LdapFluff::GenericMemberService
5
5
 
6
- attr_accessor :ldap
7
-
8
- def initialize(ldap, group_base)
9
- @ldap = ldap
10
- @group_base = group_base
6
+ def initialize(ldap, config)
7
+ @attr_login = (config.attr_login || 'uid')
8
+ super
11
9
  end
12
10
 
13
11
  # return an ldap user with groups attached
@@ -17,41 +15,16 @@ class LdapFluff::FreeIPA::MemberService
17
15
  # if group data is missing, they aren't querying with a user
18
16
  # with enough privileges
19
17
  raise InsufficientQueryPrivilegesException if user.size <= 1
20
- _group_names_from_cn(user[1][:memberof])
21
- end
22
-
23
- def find_user(uid)
24
- user = @ldap.search(:filter => name_filter(uid))
25
- raise UIDNotFoundException if (user.nil? || user.empty?)
26
- user
27
- end
28
-
29
- def find_group(gid)
30
- group = @ldap.search(:filter => group_filter(gid), :base => @group_base)
31
- raise GIDNotFoundException if (group.nil? || group.empty?)
32
- group
33
- end
34
-
35
- def name_filter(uid)
36
- Net::LDAP::Filter.eq("uid", uid)
37
- end
38
-
39
- def group_filter(gid)
40
- Net::LDAP::Filter.eq("cn", gid)
41
- end
42
-
43
- def _group_names_from_cn(grouplist)
44
- p = proc { |g| g.sub(/.*?cn=(.*?),.*/, '\1') }
45
- grouplist.collect(&p)
18
+ get_groups(user[1][:memberof])
46
19
  end
47
20
 
48
- class UIDNotFoundException < StandardError
21
+ class UIDNotFoundException < LdapFluff::Error
49
22
  end
50
23
 
51
- class GIDNotFoundException < StandardError
24
+ class GIDNotFoundException < LdapFluff::Error
52
25
  end
53
26
 
54
- class InsufficientQueryPrivilegesException < StandardError
27
+ class InsufficientQueryPrivilegesException < LdapFluff::Error
55
28
  end
56
29
 
57
30
  end
@@ -0,0 +1,75 @@
1
+ class LdapFluff::Generic
2
+ attr_accessor :ldap, :member_service
3
+
4
+ def initialize(config = {})
5
+ @ldap = Net::LDAP.new(:host => config.host,
6
+ :base => config.base_dn,
7
+ :port => config.port,
8
+ :encryption => config.encryption)
9
+ @attr_login = config.attr_login
10
+ @group_base = (config.group_base.empty? ? config.base_dn : config.group_base)
11
+ @member_service = self.class::MemberService.new(@ldap, config)
12
+ end
13
+
14
+ def user_exists?(uid)
15
+ @member_service.find_user(uid)
16
+ true
17
+ rescue self.class::MemberService::UIDNotFoundException
18
+ false
19
+ end
20
+
21
+ def group_exists?(gid)
22
+ @member_service.find_group(gid)
23
+ true
24
+ rescue self.class::MemberService::GIDNotFoundException
25
+ false
26
+ end
27
+
28
+ def groups_for_uid(uid)
29
+ @member_service.find_user_groups(uid)
30
+ rescue self.class::MemberService::UIDNotFoundException
31
+ return []
32
+ end
33
+
34
+ def users_for_gid(gid)
35
+ return [] unless group_exists?(gid)
36
+ search = @member_service.find_group(gid).last
37
+
38
+ method = [:member, :ismemberof,
39
+ :memberof, :memberuid].find { |m| search.respond_to? m } or
40
+ raise 'Group does not have any members'
41
+
42
+ users_from_search_results(search, method)
43
+ end
44
+
45
+ def includes_cn?(cn)
46
+ filter = Net::LDAP::Filter.eq('cn', cn)
47
+ @ldap.search(:base => @ldap.base, :filter => filter).present?
48
+ end
49
+
50
+ def service_bind
51
+ unless @anon || bind?(@bind_user, @bind_pass)
52
+ raise UnauthenticatedException,
53
+ "Could not bind to #{class_name} user #{@bind_user}"
54
+ end
55
+ end
56
+
57
+ private
58
+ def class_name
59
+ self.class.name.split('::').last
60
+ end
61
+
62
+ def users_from_search_results(search, method)
63
+ members = search.send method
64
+ if method == :memberuid
65
+ # memberuid contains an array ['user1','user2'], no need to parse it
66
+ members
67
+ else
68
+ @member_service.get_logins(members)
69
+ end
70
+ end
71
+
72
+ class UnauthenticatedException < LdapFluff::Error
73
+ end
74
+ end
75
+
@@ -0,0 +1,62 @@
1
+ require 'net/ldap'
2
+
3
+ class LdapFluff::GenericMemberService
4
+
5
+ attr_accessor :ldap
6
+
7
+ def initialize(ldap, config)
8
+ @ldap = ldap
9
+ @group_base = (config.group_base.empty? ? config.base_dn : config.group_base)
10
+ begin
11
+ @search_filter = Net::LDAP::Filter.construct(config.search_filter) unless (config.search_filter.nil? || config.search_filter.empty?)
12
+ rescue Net::LDAP::LdapError => error
13
+ puts "Search filter unavailable - #{error}"
14
+ end
15
+ end
16
+
17
+ def find_user(uid)
18
+ user = @ldap.search(:filter => name_filter(uid))
19
+ raise self.class::UIDNotFoundException if (user.nil? || user.empty?)
20
+ user
21
+ end
22
+
23
+ def find_group(gid)
24
+ group = @ldap.search(:filter => group_filter(gid), :base => @group_base)
25
+ raise self.class::GIDNotFoundException if (group.nil? || group.empty?)
26
+ group
27
+ end
28
+
29
+ def name_filter(uid)
30
+ filter = Net::LDAP::Filter.eq(@attr_login, uid)
31
+
32
+ if @search_filter.nil?
33
+ filter
34
+ else
35
+ filter & @search_filter
36
+ end
37
+ end
38
+
39
+ def group_filter(gid)
40
+ Net::LDAP::Filter.eq("cn", gid)
41
+ end
42
+
43
+ # extract the group names from the LDAP style response,
44
+ # return string will be something like
45
+ # CN=bros,OU=bropeeps,DC=jomara,DC=redhat,DC=com
46
+ def get_groups(grouplist)
47
+ grouplist.map(&:downcase).collect { |g| g.sub(/.*?cn=(.*?),.*/, '\1') }
48
+ end
49
+
50
+ def get_logins(userlist)
51
+ userlist.map(&:downcase!)
52
+ [@attr_login, 'uid', 'cn'].map do |attribute|
53
+ logins = userlist.collect { |g| g.sub(/.*?#{attribute}=(.*?),.*/, '\1') }
54
+ if logins == userlist
55
+ nil
56
+ else
57
+ logins
58
+ end
59
+ end.compact.flatten
60
+ end
61
+
62
+ end