ldap_fluff 0.2.5 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.

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