chuckdbacon-activedirectory 1.0.4

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.
@@ -0,0 +1,141 @@
1
+ #-- license
2
+ #
3
+ # This file is part of the Ruby Active Directory Project
4
+ # on the web at http://rubyforge.org/projects/activedirectory
5
+ #
6
+ # Copyright (c) 2008, James Hunt <filefrog@gmail.com>
7
+ # based on original code by Justin Mecham
8
+ #
9
+ # This program is free software: you can redistribute it and/or modify
10
+ # it under the terms of the GNU General Public License as published by
11
+ # the Free Software Foundation, either version 3 of the License, or
12
+ # (at your option) any later version.
13
+ #
14
+ # This program is distributed in the hope that it will be useful,
15
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
+ # GNU General Public License for more details.
18
+ #
19
+ # You should have received a copy of the GNU General Public License
20
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
21
+ #
22
+ #++ license
23
+
24
+ module ActiveDirectory
25
+ module Rails
26
+ module User
27
+ def self.included(klass)
28
+ klass.extend(ClassMethods)
29
+ klass.send(:include, InstanceMethods)
30
+ end
31
+
32
+ module InstanceMethods
33
+ # Is this Person active? Active people have valid
34
+ # usernames. Inactive people have empty usernames.
35
+ #
36
+ def active?
37
+ username != ""
38
+ end
39
+
40
+ # Whether or not this Person has a corresponding Active Directory
41
+ # account that we can synchronize with, through the PeopleSynchronizer.
42
+ #
43
+ def in_active_directory?
44
+ !guid.blank?
45
+ end
46
+
47
+ # Whether or not this Person can be authenticated with the
48
+ # given password, against Active Directory.
49
+ #
50
+ # For Active Directory authentication, we attempt to bind to the
51
+ # configured AD server as the user, and supply the password for
52
+ # authentication.
53
+ #
54
+ # There are two special cases for authentication, related to the
55
+ # environment the app is currently running in:
56
+ #
57
+ # *Development*
58
+ #
59
+ # In development, the blank password ('') will always cause this method
60
+ # to return true, thereby allowing developers to test functionality
61
+ # for a variety of roles.
62
+ #
63
+ # *Training*
64
+ #
65
+ # In training, a special training password ('trainme') will always
66
+ # cause this method to return true, thereby allowing trainers to
67
+ # use other people accounts to illustrate certain restricted processes.
68
+ #
69
+ def authenticates?(password)
70
+ # Never allow inactive users.
71
+ return false unless active?
72
+
73
+ # Allow blank password for any account in development.
74
+ return true if password == "" and ENV['RAILS_ENV'] == 'development'
75
+ return true if password == "trainme" and ENV['RAILS_ENV'] == 'training'
76
+
77
+ # Don't go against AD unless we really mean it.
78
+ return false unless ENV['RAILS_ENV'] == 'production'
79
+
80
+ # If they are not in AD, fail.
81
+ return false unless in_active_directory?
82
+
83
+ ad_user = ActiveDirectory::User.find_by_sAMAccountName(self.username)
84
+ ad_user and ad_user.authenticate(password)
85
+ end
86
+
87
+ def active_directory_equivalent=(ad_user)
88
+ return unless ad_user
89
+ update_attributes(
90
+ :first_name => ad_user.givenName,
91
+ :middle_name => ad_user.initials,
92
+ :last_name => ad_user.sn,
93
+ :username => ad_user.sAMAccountName,
94
+ :email => ad_user.mail,
95
+ :guid => ad_user.objectGUID
96
+ )
97
+ end
98
+ end
99
+
100
+ module ClassMethods
101
+ # Attempt to authenticate someone with a username and password.
102
+ # This method properly handles both local store users and AD
103
+ # users.
104
+ #
105
+ # If the username is valid, and the password matches the username,
106
+ # the Person object corresponding to the username is return.
107
+ #
108
+ # Otherwise, nil is returned, to indicate an authentication failure.
109
+ #
110
+ def authenticate(username, password)
111
+ person = find_by_username(username)
112
+ return person if (person and person.authenticates?(password))
113
+ nil
114
+ end
115
+
116
+ # Retrieves all of the Person objects that have corresponding
117
+ # Active Directory accounts. This method does not contact
118
+ # the AD servers to retrieve the AD objects -- that is left up
119
+ # to the caller.
120
+ #
121
+ def in_active_directory
122
+ find(:all, :conditions => 'guid IS NOT NULL AND guid != ""')
123
+ end
124
+
125
+ # Retrieves all Person objects that are currently active,
126
+ # meaning they have not been disabled by PeopleSynchronizer.
127
+ #
128
+ def active
129
+ find(:all, :conditions => 'username != ""')
130
+ end
131
+
132
+ # Retrieves all Person objects that are currently inactive,
133
+ # meaning they have been disabled by PeopleSynchronizer.
134
+ #
135
+ def inactive
136
+ find(:all, :conditions => 'username = ""')
137
+ end
138
+ end
139
+ end # module User
140
+ end # module Rails
141
+ end #module ActiveDirectory
@@ -0,0 +1,46 @@
1
+ #-- license
2
+ #
3
+ # This file is part of the Ruby Active Directory Project
4
+ # on the web at http://rubyforge.org/projects/activedirectory
5
+ #
6
+ # Copyright (c) 2008, James Hunt <filefrog@gmail.com>
7
+ # based on original code by Justin Mecham
8
+ #
9
+ # This program is free software: you can redistribute it and/or modify
10
+ # it under the terms of the GNU General Public License as published by
11
+ # the Free Software Foundation, either version 3 of the License, or
12
+ # (at your option) any later version.
13
+ #
14
+ # This program is distributed in the hope that it will be useful,
15
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
+ # GNU General Public License for more details.
18
+ #
19
+ # You should have received a copy of the GNU General Public License
20
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
21
+ #
22
+ #++ license
23
+
24
+ module ActiveDirectory
25
+ class Timestamp
26
+ AD_DIVISOR = 10_000_000 #:nodoc:
27
+ AD_OFFSET = 11_644_473_600 #:nodoc:
28
+
29
+ #
30
+ # Encodes a local Time object (or the number of seconds since January
31
+ # 1, 1970) into a timestamp that the Active Directory server can
32
+ # understand (number of 100 nanosecond time units since January 1, 1600)
33
+ #
34
+ def self.encode(local_time)
35
+ (local_time.to_i + AD_OFFSET) * AD_DIVISOR
36
+ end
37
+
38
+ #
39
+ # Decodes an Active Directory timestamp (the number of 100 nanosecond time
40
+ # units since January 1, 1600) into a Ruby Time object.
41
+ #
42
+ def self.decode(remote_time)
43
+ Time.at( (remote_time.to_i / AD_DIVISOR) - AD_OFFSET )
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,155 @@
1
+ #-- license
2
+ #
3
+ # This file is part of the Ruby Active Directory Project
4
+ # on the web at http://rubyforge.org/projects/activedirectory
5
+ #
6
+ # Copyright (c) 2008, James Hunt <filefrog@gmail.com>
7
+ # based on original code by Justin Mecham
8
+ #
9
+ # This program is free software: you can redistribute it and/or modify
10
+ # it under the terms of the GNU General Public License as published by
11
+ # the Free Software Foundation, either version 3 of the License, or
12
+ # (at your option) any later version.
13
+ #
14
+ # This program is distributed in the hope that it will be useful,
15
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
+ # GNU General Public License for more details.
18
+ #
19
+ # You should have received a copy of the GNU General Public License
20
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
21
+ #
22
+ #++ license
23
+
24
+ module ActiveDirectory
25
+ class User < Base
26
+ include Member
27
+
28
+ UAC_ACCOUNT_DISABLED = 0x0002
29
+ UAC_NORMAL_ACCOUNT = 0x0200 # 512
30
+
31
+ def self.filter # :nodoc:
32
+ Net::LDAP::Filter.eq(:objectClass,'user') & ~Net::LDAP::Filter.eq(:objectClass,'computer')
33
+ end
34
+
35
+ def self.required_attributes #:nodoc:
36
+ { :objectClass => ['top', 'organizationalPerson', 'person', 'user'] }
37
+ end
38
+
39
+ #
40
+ # Try to authenticate the current User against Active Directory
41
+ # using the supplied password. Returns false upon failure.
42
+ #
43
+ # Authenticate can fail for a variety of reasons, primarily:
44
+ #
45
+ # * The password is wrong
46
+ # * The account is locked
47
+ # * The account is disabled
48
+ #
49
+ # User#locked? and User#disabled? can be used to identify the
50
+ # latter two cases, and if the account is enabled and unlocked,
51
+ # Athe password is probably invalid.
52
+ #
53
+ def authenticate(password)
54
+ return false if password.to_s.empty?
55
+
56
+ auth_ldap = @@ldap.dup.bind_as(
57
+ :filter => "(sAMAccountName=#{sAMAccountName})",
58
+ :password => password
59
+ )
60
+ end
61
+
62
+ #
63
+ # Return the User's manager (another User object), depending on
64
+ # what is stored in the manager attribute.
65
+ #
66
+ # Returns nil if the schema does not include the manager attribute
67
+ # or if no manager has been configured.
68
+ #
69
+ def manager
70
+ return nil if @entry.manager.nil?
71
+ User.find_by_distinguishedName(@entry.manager.to_s)
72
+ end
73
+
74
+ #
75
+ # Returns an array of Group objects that this User belongs to.
76
+ # Only the immediate parent groups are returned, so if the user
77
+ # Sally is in a group called Sales, and Sales is in a group
78
+ # called Marketting, this method would only return the Sales group.
79
+ #
80
+ def groups
81
+ @groups ||= memberOf.collect { |dn| Group.find_by_distinguishedName(dn) }
82
+ end
83
+
84
+ #
85
+ # Returns an array of User objects that have this
86
+ # User as their manager.
87
+ #
88
+ def direct_reports
89
+ return [] if @entry.directReports.nil?
90
+ @direct_reports ||= @entry.directReports.collect { |dn| User.find_by_distinguishedName(dn) }
91
+ end
92
+
93
+ #
94
+ # Returns true if this account has been locked out
95
+ # (usually because of too many invalid authentication attempts).
96
+ #
97
+ # Locked accounts can be unlocked with the User#unlock! method.
98
+ #
99
+ def locked?
100
+ !lockoutTime.nil? && lockoutTime.to_i != 0
101
+ end
102
+
103
+ #
104
+ # Returns true if this account has been disabled.
105
+ #
106
+ def disabled?
107
+ userAccountControl.to_i & UAC_ACCOUNT_DISABLED != 0
108
+ end
109
+
110
+ #
111
+ # Returns true if the user should be able to log in with a correct
112
+ # password (essentially, their account is not disabled or locked
113
+ # out).
114
+ #
115
+ def can_login?
116
+ !disabled? && !locked?
117
+ end
118
+
119
+ #
120
+ # Change the password for this account.
121
+ #
122
+ # This operation requires that the bind user specified in
123
+ # Base.setup have heightened privileges. It also requires an
124
+ # SSL connection.
125
+ #
126
+ # If the force_change argument is passed as true, the password will
127
+ # be marked as 'expired', forcing the user to change it the next
128
+ # time they successfully log into the domain.
129
+ #
130
+ def change_password(new_password, force_change = false)
131
+ settings = @@settings.dup.merge({
132
+ :port => 636,
133
+ :encryption => { :method => :simple_tls }
134
+ })
135
+
136
+ ldap = Net::LDAP.new(settings)
137
+ ldap.modify(
138
+ :dn => distinguishedName,
139
+ :operations => [
140
+ [ :replace, :lockoutTime, [ '0' ] ],
141
+ [ :replace, :unicodePwd, [ Password.encode(new_password) ] ],
142
+ [ :replace, :userAccountControl, [ UAC_NORMAL_ACCOUNT.to_s ] ],
143
+ [ :replace, :pwdLastSet, [ (force_change ? '0' : '-1') ] ]
144
+ ]
145
+ )
146
+ end
147
+
148
+ #
149
+ # Unlocks this account.
150
+ #
151
+ def unlock!
152
+ @@ldap.replace_attribute(distinguishedName, :lockoutTime, ['0'])
153
+ end
154
+ end
155
+ end
metadata ADDED
@@ -0,0 +1,69 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: chuckdbacon-activedirectory
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.4
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - James R Hunt
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-11-04 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: net-ldap
16
+ requirement: &70256844765740 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 0.1.1
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70256844765740
25
+ description: ActiveDirectory uses Net::LDAP to provide a means of accessing and modifying
26
+ an Active Directory data store.
27
+ email: filefrog@gmail.com
28
+ executables: []
29
+ extensions: []
30
+ extra_rdoc_files:
31
+ - README
32
+ files:
33
+ - lib/active_directory.rb
34
+ - lib/active_directory/base.rb
35
+ - lib/active_directory/computer.rb
36
+ - lib/active_directory/container.rb
37
+ - lib/active_directory/group.rb
38
+ - lib/active_directory/member.rb
39
+ - lib/active_directory/password.rb
40
+ - lib/active_directory/rails/synchronizer.rb
41
+ - lib/active_directory/rails/user.rb
42
+ - lib/active_directory/timestamp.rb
43
+ - lib/active_directory/user.rb
44
+ - README
45
+ homepage: http://github.com/filefrog/activedirectory
46
+ licenses: []
47
+ post_install_message:
48
+ rdoc_options: []
49
+ require_paths:
50
+ - lib
51
+ required_ruby_version: !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ! '>='
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ required_rubygems_version: !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ! '>='
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ requirements: []
64
+ rubyforge_project:
65
+ rubygems_version: 1.8.11
66
+ signing_key:
67
+ specification_version: 3
68
+ summary: An interface library for accessing Microsoft's Active Directory.
69
+ test_files: []