devise_active_directory_authenticatable 0.3.3 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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
-