devise_active_directory_authenticatable 0.3.3 → 0.4.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.
data/README.md CHANGED
@@ -17,7 +17,7 @@ These gems are dependencies of the gem:
17
17
 
18
18
  - Devise 1.1.5
19
19
  - active_directory 1.2.4
20
- - ancestry 1.2.3 _(For storing the group heirarchy)_
20
+ - activerecord-import 0.2.0
21
21
 
22
22
  Installation
23
23
  ------------
@@ -57,12 +57,16 @@ Options:
57
57
 
58
58
  The rest of this documentation needs to be revised. To get going on this, run the installer which will add some configuration options to config/intializers/devise.rb
59
59
 
60
+ Update your user and group tables in the database with migrations. Check attributes that are set in config/initializers/devise.rb to see which ones you will have to add.
61
+
60
62
  In your user model add:
61
- devise :ad_user
63
+
64
+ devise :ad_user
62
65
 
63
66
  In your group model add:
64
- devise :ad_group
65
67
 
68
+ devise :ad_group
69
+
66
70
 
67
71
  Usage
68
72
  -----
@@ -106,6 +110,8 @@ In initializer `config/initializers/devise.rb` :
106
110
  * ad\_update\_user\_memberships _(default: true)_ _[unimplemented]_
107
111
  * If true, devise will allow the memberships for groups and users to be updated. It will also update the memberships when a user logs in.
108
112
 
113
+ * ad\_caching _(default: true)_
114
+ * If true, this will instruct the plugin to use the active_directory caching feature. This greatly speeds up queries that are using the distinguishedname such as querying for group and user memberships.
109
115
 
110
116
 
111
117
  References
data/Rakefile CHANGED
@@ -1,3 +1,4 @@
1
+ require 'psych' #fix a bug with hoe
1
2
  require 'rake'
2
3
  require 'rake/testtask'
3
4
  require 'rake/rdoctask'
@@ -22,7 +23,7 @@ begin
22
23
  gemspec.homepage = "http://github.com/ajrkerr/devise_activedirectory_authenticatable"
23
24
  gemspec.authors = ["Adam Kerr"]
24
25
  gemspec.add_dependency "devise", ">= 1.1.5"
25
- gemspec.add_dependency "active_directory", ">= 1.2.4"
26
+ gemspec.add_dependency "active_directory", ">= 1.5.1"
26
27
  end
27
28
  Jeweler::GemcutterTasks.new
28
29
  rescue LoadError
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.3
1
+ 0.4.0
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{devise_active_directory_authenticatable}
8
- s.version = "0.3.3"
8
+ s.version = "0.4.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Adam Kerr"]
12
- s.date = %q{2011-02-23}
12
+ s.date = %q{2011-03-03}
13
13
  s.description = %q{Active Directory authentication module for Devise, based off of LDAP Authentication}
14
14
  s.email = %q{ajrkerr@gmail.com}
15
15
  s.extra_rdoc_files = [
@@ -34,7 +34,7 @@ Gem::Specification.new do |s|
34
34
  ]
35
35
  s.homepage = %q{http://github.com/ajrkerr/devise_activedirectory_authenticatable}
36
36
  s.require_paths = ["lib"]
37
- s.rubygems_version = %q{1.5.2}
37
+ s.rubygems_version = %q{1.6.0}
38
38
  s.summary = %q{Active Directory authentication module for Devise}
39
39
 
40
40
  if s.respond_to? :specification_version then
@@ -42,14 +42,14 @@ Gem::Specification.new do |s|
42
42
 
43
43
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
44
44
  s.add_runtime_dependency(%q<devise>, [">= 1.1.5"])
45
- s.add_runtime_dependency(%q<active_directory>, [">= 1.2.4"])
45
+ s.add_runtime_dependency(%q<active_directory>, [">= 1.5.1"])
46
46
  else
47
47
  s.add_dependency(%q<devise>, [">= 1.1.5"])
48
- s.add_dependency(%q<active_directory>, [">= 1.2.4"])
48
+ s.add_dependency(%q<active_directory>, [">= 1.5.1"])
49
49
  end
50
50
  else
51
51
  s.add_dependency(%q<devise>, [">= 1.1.5"])
52
- s.add_dependency(%q<active_directory>, [">= 1.2.4"])
52
+ s.add_dependency(%q<active_directory>, [">= 1.5.1"])
53
53
  end
54
54
  end
55
55
 
@@ -1,6 +1,7 @@
1
1
  # encoding: utf-8
2
2
  require 'devise'
3
3
  require 'active_directory'
4
+ require 'active_record'
4
5
 
5
6
  require 'devise_active_directory_authenticatable/exception'
6
7
  require 'devise_active_directory_authenticatable/logger'
@@ -80,9 +81,13 @@ module Devise
80
81
  ##Update the user memberships from the AD
81
82
  mattr_accessor :ad_update_user_memberships
82
83
  @@ad_update_user_memberships = true
84
+
85
+ ##Enable Active Directory caching. This speeds up group/membership queries significantly.
86
+ mattr_accessor :ad_caching
87
+ @@ad_caching = true
83
88
  end
84
89
 
85
- # Add ldap_authenticatable strategy to defaults.
90
+ # Add active_directory_authenticatable strategy to defaults.
86
91
  #
87
92
  Devise.add_module(:ad_user,
88
93
  :route => :session, ## This will add the routes, rather than in the routes.rb
@@ -9,6 +9,34 @@ module Devise
9
9
  extend ActiveSupport::Concern
10
10
  include AdObject
11
11
 
12
+ #Remember to check for cycles in the graph
13
+ #BFS or DFS?
14
+ def find_all_parents
15
+ end
16
+
17
+ def find_all_children
18
+ end
19
+
20
+ #Remember to check for cycles in the graph
21
+ #BFS or DFS?
22
+ def find_all_members type = :all
23
+ case type
24
+ when :all
25
+
26
+ when :users
27
+
28
+ when :groups
29
+
30
+ else
31
+ throw "Invalid argument"
32
+ end
33
+ end
34
+
35
+
36
+
37
+
38
+ #Perhaps build a translation layer for the current way attributes are synced
39
+
12
40
  module ClassMethods
13
41
  # TODO find a way to get rid of this with metaprogramming
14
42
  def devise_model
@@ -18,13 +46,6 @@ module Devise
18
46
  def activedirectory_class
19
47
  ActiveDirectory::Group
20
48
  end
21
-
22
- def sync_all
23
- return false unless connected_to_activedirectory?
24
- find_or_create_from_activedirectory.each do |gp|
25
- gp.save
26
- end
27
- end
28
49
  end
29
50
 
30
51
  end
@@ -5,70 +5,162 @@ module Devise
5
5
 
6
6
  Logger = DeviseActiveDirectoryAuthenticatable::Logger
7
7
 
8
+ def attr_map
9
+ @attr_map ||= ::Devise.ad_attr_mapping[klass.devise_model_name.to_sym]
10
+ end
11
+
8
12
  def klass
9
13
  self.class
10
14
  end
11
15
 
16
+ def ad_obj
17
+ @ad_obj ||= klass.find_activedirectory_objs(:objectguid => self.objectguid).first
18
+ end
19
+
12
20
  # Update the attributes of the current object from the AD
13
21
  # Defaults to current user if no parameters given
14
22
  def activedirectory_sync! params = {}
15
23
  params[:objectguid] = self.objectguid if params.empty?
16
- ad_obj = params[:object] || klass.find_in_activedirectory(params).first
17
- copy_from_activedirectory ad_obj unless ad_obj.nil?
24
+ @ad_obj ||= params[:object] || klass.find_activedirectory_objs(params).first
25
+ copy_from_activedirectory @ad_obj unless @ad_obj.nil?
26
+ update_memberships
18
27
  end
19
28
 
20
29
  # Update the attributes of the current object from the AD
21
30
  # Defaults to current user if no parameters given
22
- def update_from_activedirectory! params = {}
23
- params[:objectguid] = self.objectguid if params.empty?
24
- ad_obj = params[:object] || klass.find_in_activedirectory(params).first
25
- copy_from_activedirectory ad_obj unless ad_obj.nil?
31
+ def copy_from_activedirectory! params = {}
32
+ #Allows for caching, object, or AD search
33
+ params[:objectguid] = self[:objectguid] if params.empty?
34
+ @ad_obj ||= params[:object] || klass.find_activedirectory_objs(params).first
35
+ copy_from_activedirectory @ad_obj unless @ad_obj.nil?
36
+ # update_memberships
26
37
  end
27
38
 
39
+
40
+
28
41
  # Update the local object using an Active Directory entry
29
42
  def copy_from_activedirectory ad_obj
30
- ::Devise.ad_attr_mapping[klass.devise_model_name.to_sym].each do |local_attr, active_directory_attr|
31
- self[local_attr] = ad_obj.send(active_directory_attr)
43
+ attr_map.each do |local_attr, active_directory_attr|
44
+ self[local_attr] = ad_obj[active_directory_attr]
45
+ end
46
+ end
47
+
48
+ ##
49
+ # Updates the members and memberofs stored in the database
50
+ # and by update, I mean overwrite
51
+ # TODO find a way to update, or at least not molest non-AD associations
52
+ def update_memberships
53
+ update_children
54
+ update_parents
55
+ end
56
+
57
+ def update_children
58
+ #Set the members
59
+ if ad_obj[:member].is_a? Array
60
+ update_membership(ad_obj[:member], klass.member_users) if defined? klass.member_users
61
+ update_membership(ad_obj[:member], klass.member_groups) if defined? klass.member_groups
62
+ end
63
+ end
64
+
65
+ def update_parents
66
+ # MemberOf Relationship
67
+ if ad_obj[:memberof].is_a? Array and defined? klass.memberof
68
+ update_membership(ad_obj[:memberof], klass.memberof)
32
69
  end
33
70
  end
34
71
 
35
- def find_in_activedirectory
36
- klass.find_in_activedirectory :objectGUID => objectGUID
72
+ def update_membership ad_objs, params
73
+ # Create the objects of the right type, then sets them
74
+ klass = params[:class]
75
+ field = params[:field]
76
+
77
+ ad_objs = klass.find_from_activedirectory(:object => ad_objs)
78
+ self.send "#{field}=", ad_objs
37
79
  end
38
80
 
39
81
  module ClassMethods
40
82
 
83
+ attr_accessor :member_groups, :member_users, :memberof
84
+
41
85
  def login_with
42
86
  ::Devise.authentication_keys.first
43
87
  end
44
88
 
89
+ def set_devise_ad_options field, params = {}
90
+ ret = {}
91
+ ret[:field] = field.to_s
92
+ ret[:class_name] = (params[:class_name] || field).to_s.classify
93
+ ret[:class] = Kernel.const_get(ret[:class_name])
94
+
95
+ unless ret[:class].include? AdObject
96
+ raise "#{ret[:class_name]} does not include any of the Devise Active Directory modules. Please consult the documentation."
97
+ end
98
+
99
+ return ret
100
+ end
101
+
102
+ def devise_ad_memberof field, params = {}
103
+ @memberof = set_devise_ad_options field, params
104
+ end
105
+
106
+ def devise_ad_member_groups field, params = {}
107
+ @member_groups = set_devise_ad_options field, params
108
+ end
109
+
110
+ def devise_ad_member_users field, params = {}
111
+ @member_users = set_devise_ad_options field, params
112
+ end
113
+
45
114
  def devise_model_name
46
- devise_model.name[/.*::(.*)/, 1]
115
+ @devise_model ||= devise_model.name[/.*::(.*)/, 1]
47
116
  end
48
117
 
49
118
  def activedirectory_class_name
50
- activedirectory_class.name[/.*::(.*)/, 1]
119
+ @ad_class ||= activedirectory_class.name[/.*::(.*)/, 1]
51
120
  end
52
121
 
122
+
53
123
  #Search based on GUID, DN or Username primarily
54
- def find_in_activedirectory(local_params = {})
124
+ def find_activedirectory_objs local_params = {}
125
+ #Sometimes we're provide the objects
126
+ if local_params.key? :object
127
+ return [local_params[:object]] unless local_params[:object].kind_of? Array
128
+ return local_params[:object]
129
+ end
130
+
55
131
  #Reverse mappings for user
56
- ad_params = local_attrs_to_ad local_params
57
- Logger.send "Translated #{local_params.inspect} to #{ad_params.inspect}"
58
- return find_all_in_activedirectory if ad_params.empty?
132
+ ad_params = local_attrs_to_ad(local_params)
59
133
 
60
134
  activedirectory_class.find(:all, ad_params)
61
135
  end
62
136
 
63
- def find_or_create_from_activedirectory params = {}
64
- ad_objs = find_in_activedirectory params
137
+ def find_from_activedirectory local_params = {}
138
+ ad_objs = find_activedirectory_objs local_params
139
+ guids = ad_objs.collect { |obj| obj[:objectguid] }
140
+ scoped.where(:objectguid => guids)
141
+ end
142
+
143
+ ##
144
+ # Does a search using AD terms and either finds the corresponding
145
+ # object in the database, or creates it
146
+ # TODO change attributes to not be statically mapped to objectguid
147
+ def find_or_create_from_activedirectory local_params = {}
148
+ ad_objs = find_activedirectory_objs local_params
65
149
  local_objs = []
66
150
 
67
- ad_objs.each do |ad_obj|
68
- obj = scoped.where(:objectguid => ad_obj.objectguid).first
69
- obj = new if obj.blank?
151
+ #Grab all of the objects in one query by GUID for efficiency
152
+ guids = ad_objs.collect { |obj| obj[:objectguid] }
153
+ db_objs_by_guid = {}
154
+
155
+ #Make a hash map to do quick lookups
156
+ scoped.where(:objectguid => guids).each do |db_obj|
157
+ db_objs_by_guid[db_obj.objectguid] = db_obj
158
+ end
70
159
 
71
- obj.activedirectory_sync! :object => ad_obj
160
+ ad_objs.each do |ad_obj|
161
+ guid = ad_obj[:objectguid]
162
+ obj = db_objs_by_guid[guid] || new
163
+ obj.copy_from_activedirectory!(:object => ad_obj) if obj.new_record?
72
164
 
73
165
  local_objs << obj
74
166
  end
@@ -76,21 +168,41 @@ module Devise
76
168
  local_objs
77
169
  end
78
170
 
79
- def find_all_in_activedirectory
80
- activedirectory_class.find(:all)
171
+ def sync_all
172
+ return false unless connected_to_activedirectory?
173
+
174
+ db_objs = find_or_create_from_activedirectory
175
+
176
+ ActiveRecord::Base.transaction do
177
+ #Save the new ones
178
+ db_objs.each { |obj| obj.save if obj.new_record? }
179
+
180
+ #Then update the memberships
181
+ #If we're updating all of them, then updating just the parents will do
182
+ db_objs.each do |obj|
183
+ obj.update_parents
184
+ end
185
+ end
81
186
  end
82
187
 
188
+ ##
189
+ # Checks to see if a conection with AD has been established
83
190
  def connected_to_activedirectory?
84
191
  ActiveDirectory::Base.connected?
85
192
  end
86
193
 
87
- # Initializes connection with active directory
194
+ ##
195
+ # Sets the username and password for the connection
196
+ # params {:username => 'joe.user', :password => 'top_secret' }
88
197
  def set_activedirectory_credentials(params = {})
89
- #Used for username and password
198
+ #Used for username and password only
90
199
  ::Devise.ad_settings[:auth].merge! params
91
200
  end
92
201
 
202
+ ##
203
+ # Attempts to connect with the activedirectory based on the configuration options
93
204
  def activedirectory_connect
205
+ ActiveDirectory::Base.enable_cache if ::Devise.ad_caching
94
206
  ActiveDirectory::Base.setup(::Devise.ad_settings)
95
207
  raise DeviseActiveDirectoryAuthenticatable::ActiveDirectoryException, "Invalid Username or Password" unless ActiveDirectory::Base.connected?
96
208
  end
@@ -12,19 +12,14 @@ module Devise
12
12
 
13
13
  Logger = DeviseActiveDirectoryAuthenticatable::Logger
14
14
 
15
- included do
16
- #has_and_belongs_to_many :member, :class_name, :join_table
17
- end
18
-
19
15
  ## Devise key
20
16
  def login_with
21
17
  self[::Devise.authentication_keys.first]
22
18
  end
23
19
 
24
20
  # Login event handler. Triggered after authentication.
21
+ # Maybe
25
22
  def login
26
- activedirectory_sync!
27
-
28
23
  super if defined? super
29
24
  end
30
25
 
@@ -55,10 +50,6 @@ module Devise
55
50
  activedirectory_connect
56
51
  Logger.send "Attempt Result: #{ActiveDirectory::Base.error}"
57
52
 
58
-
59
- # ad_user = find_in_activedirectory(@login_with => username)
60
- # return false unless ad_user
61
-
62
53
  # Find them in the local database
63
54
  user = find_or_create_from_activedirectory(login_with => attributes[login_with]).first
64
55
 
@@ -91,6 +91,8 @@ module DeviseActiveDirectoryAuthenticatable
91
91
  ##Update the user memberships from the AD
92
92
  # config.ad_update_user_memberships = true
93
93
 
94
+ ##Uses caching when doing AD queries for DNs. This speeds things up significantly.
95
+ # config.ad_caching = true
94
96
  eof
95
97
 
96
98
  settings
metadata CHANGED
@@ -1,64 +1,47 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: devise_active_directory_authenticatable
3
- version: !ruby/object:Gem::Version
4
- hash: 21
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.4.0
5
5
  prerelease:
6
- segments:
7
- - 0
8
- - 3
9
- - 3
10
- version: 0.3.3
11
6
  platform: ruby
12
- authors:
7
+ authors:
13
8
  - Adam Kerr
14
9
  autorequire:
15
10
  bindir: bin
16
11
  cert_chain: []
17
-
18
- date: 2011-02-23 00:00:00 -05:00
12
+ date: 2011-03-03 00:00:00.000000000 -05:00
19
13
  default_executable:
20
- dependencies:
21
- - !ruby/object:Gem::Dependency
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
22
16
  name: devise
23
- prerelease: false
24
- requirement: &id001 !ruby/object:Gem::Requirement
17
+ requirement: &2153561640 !ruby/object:Gem::Requirement
25
18
  none: false
26
- requirements:
27
- - - ">="
28
- - !ruby/object:Gem::Version
29
- hash: 25
30
- segments:
31
- - 1
32
- - 1
33
- - 5
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
34
22
  version: 1.1.5
35
23
  type: :runtime
36
- version_requirements: *id001
37
- - !ruby/object:Gem::Dependency
38
- name: active_directory
39
24
  prerelease: false
40
- requirement: &id002 !ruby/object:Gem::Requirement
25
+ version_requirements: *2153561640
26
+ - !ruby/object:Gem::Dependency
27
+ name: active_directory
28
+ requirement: &2153561160 !ruby/object:Gem::Requirement
41
29
  none: false
42
- requirements:
43
- - - ">="
44
- - !ruby/object:Gem::Version
45
- hash: 23
46
- segments:
47
- - 1
48
- - 2
49
- - 4
50
- version: 1.2.4
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: 1.5.1
51
34
  type: :runtime
52
- version_requirements: *id002
53
- description: Active Directory authentication module for Devise, based off of LDAP Authentication
35
+ prerelease: false
36
+ version_requirements: *2153561160
37
+ description: Active Directory authentication module for Devise, based off of LDAP
38
+ Authentication
54
39
  email: ajrkerr@gmail.com
55
40
  executables: []
56
-
57
41
  extensions: []
58
-
59
- extra_rdoc_files:
42
+ extra_rdoc_files:
60
43
  - README.md
61
- files:
44
+ files:
62
45
  - MIT-LICENSE
63
46
  - README.md
64
47
  - Rakefile
@@ -77,36 +60,26 @@ files:
77
60
  has_rdoc: true
78
61
  homepage: http://github.com/ajrkerr/devise_activedirectory_authenticatable
79
62
  licenses: []
80
-
81
63
  post_install_message:
82
64
  rdoc_options: []
83
-
84
- require_paths:
65
+ require_paths:
85
66
  - lib
86
- required_ruby_version: !ruby/object:Gem::Requirement
67
+ required_ruby_version: !ruby/object:Gem::Requirement
87
68
  none: false
88
- requirements:
89
- - - ">="
90
- - !ruby/object:Gem::Version
91
- hash: 3
92
- segments:
93
- - 0
94
- version: "0"
95
- required_rubygems_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ! '>='
71
+ - !ruby/object:Gem::Version
72
+ version: '0'
73
+ required_rubygems_version: !ruby/object:Gem::Requirement
96
74
  none: false
97
- requirements:
98
- - - ">="
99
- - !ruby/object:Gem::Version
100
- hash: 3
101
- segments:
102
- - 0
103
- version: "0"
75
+ requirements:
76
+ - - ! '>='
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
104
79
  requirements: []
105
-
106
80
  rubyforge_project:
107
- rubygems_version: 1.5.2
81
+ rubygems_version: 1.6.0
108
82
  signing_key:
109
83
  specification_version: 3
110
84
  summary: Active Directory authentication module for Devise
111
85
  test_files: []
112
-