judy-activedirectory 1.1.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/lib/active_directory.rb +39 -0
- data/lib/active_directory/base.rb +405 -0
- data/lib/active_directory/computer.rb +15 -0
- data/lib/active_directory/container.rb +94 -0
- data/lib/active_directory/group.rb +145 -0
- data/lib/active_directory/member.rb +33 -0
- data/lib/active_directory/ou.rb +164 -0
- data/lib/active_directory/password.rb +19 -0
- data/lib/active_directory/rails/synchronizer.rb +211 -0
- data/lib/active_directory/rails/user.rb +118 -0
- data/lib/active_directory/timestamp.rb +23 -0
- data/lib/active_directory/user.rb +142 -0
- metadata +73 -0
@@ -0,0 +1,94 @@
|
|
1
|
+
module ActiveDirectory
|
2
|
+
#
|
3
|
+
# The ActiveDirectory::Container class represents a more malleable way
|
4
|
+
# of dealing with LDAP Distinguished Names (dn), like
|
5
|
+
# "cn=UserName,ou=Users,dc=example,dc=org".
|
6
|
+
#
|
7
|
+
# The following two representations of the above dn are identical:
|
8
|
+
#
|
9
|
+
# dn = "cn=UserName,ou=Users,dc=example,dc=org"
|
10
|
+
# dn = ActiveDirectory::Container.dc('org').dc('example').ou('Users').cn('UserName').to_s
|
11
|
+
#
|
12
|
+
class Container
|
13
|
+
attr_reader :type
|
14
|
+
attr_reader :name
|
15
|
+
attr_reader :parent
|
16
|
+
|
17
|
+
def initialize(type, name, node = nil) #:nodoc:
|
18
|
+
@type = type
|
19
|
+
@name = name
|
20
|
+
@node = node
|
21
|
+
end
|
22
|
+
|
23
|
+
#
|
24
|
+
# Creates a starting OU (Organizational Unit) dn part.
|
25
|
+
#
|
26
|
+
# # ou_part = "ou=OrganizationalUnit"
|
27
|
+
# ou_part = ActiveDirectory::Container.ou('OrganizationalUnit').to_s
|
28
|
+
#
|
29
|
+
def self.ou(name)
|
30
|
+
new(:ou, name, nil)
|
31
|
+
end
|
32
|
+
|
33
|
+
#
|
34
|
+
# Creates a starting DC (Domain Component) dn part.
|
35
|
+
#
|
36
|
+
# # dc_part = "dc=net"
|
37
|
+
# dc_part = ActiveDirectory::Container.dc('net').to_s
|
38
|
+
#
|
39
|
+
def self.dc(name)
|
40
|
+
new(:dc, name, nil)
|
41
|
+
end
|
42
|
+
|
43
|
+
#
|
44
|
+
# Creates a starting CN (Canonical Name) dn part.
|
45
|
+
#
|
46
|
+
# # cn_part = "cn=CanonicalName"
|
47
|
+
# cn_part = ActiveDirectory::Container.cn('CanonicalName').to_s
|
48
|
+
#
|
49
|
+
def self.cn(name)
|
50
|
+
new(:cn, name, nil)
|
51
|
+
end
|
52
|
+
|
53
|
+
#
|
54
|
+
# Appends an OU (Organizational Unit) dn part to another Container.
|
55
|
+
#
|
56
|
+
# # ou = "ou=InfoTech,dc=net"
|
57
|
+
# ou = ActiveDirectory::Container.dc("net").ou("InfoTech").to_s
|
58
|
+
#
|
59
|
+
def ou(name)
|
60
|
+
self.class.new(:ou, name, self)
|
61
|
+
end
|
62
|
+
|
63
|
+
#
|
64
|
+
# Appends a DC (Domain Component) dn part to another Container.
|
65
|
+
#
|
66
|
+
# # base = "dc=example,dc=net"
|
67
|
+
# base = ActiveDirectory::Container.dc("net").dc("example").to_s
|
68
|
+
#
|
69
|
+
def dc(name)
|
70
|
+
self.class.new(:dc, name, self)
|
71
|
+
end
|
72
|
+
|
73
|
+
#
|
74
|
+
# Appends a CN (Canonical Name) dn part to another Container.
|
75
|
+
#
|
76
|
+
# # user = "cn=UID,ou=Users"
|
77
|
+
# user = ActiveDirectory::Container.ou("Users").cn("UID")
|
78
|
+
#
|
79
|
+
def cn(name)
|
80
|
+
self.class.new(:cn, name, self)
|
81
|
+
end
|
82
|
+
|
83
|
+
#
|
84
|
+
# Converts the Container object to its String representation.
|
85
|
+
#
|
86
|
+
def to_s
|
87
|
+
@node ? "#{@type}=#{name},#{@node.to_s}" : "#{@type}=#{name}"
|
88
|
+
end
|
89
|
+
|
90
|
+
def ==(other) #:nodoc:
|
91
|
+
to_s.downcase == other.to_s.downcase
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
module ActiveDirectory
|
2
|
+
class Group < Base
|
3
|
+
include Member
|
4
|
+
|
5
|
+
def self.filter # :nodoc:
|
6
|
+
Net::LDAP::Filter.eq(:objectClass,'group')
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.required_attributes # :nodoc:
|
10
|
+
{ :objectClass => [ 'top', 'group' ] }
|
11
|
+
end
|
12
|
+
|
13
|
+
def reload # :nodoc:
|
14
|
+
@member_users_non_r = nil
|
15
|
+
@member_users_r = nil
|
16
|
+
@member_groups_non_r = nil
|
17
|
+
@member_groups_r = nil
|
18
|
+
@groups = nil
|
19
|
+
super
|
20
|
+
end
|
21
|
+
|
22
|
+
#
|
23
|
+
# Returns true if the passed User or Group object belongs to
|
24
|
+
# this group. For performance reasons, the check is handled
|
25
|
+
# by the User or Group object passed.
|
26
|
+
#
|
27
|
+
def has_member?(user)
|
28
|
+
user.member_of?(self)
|
29
|
+
end
|
30
|
+
|
31
|
+
#
|
32
|
+
# Add the passed User or Group object to this Group. Returns true if
|
33
|
+
# the User or Group is already a member of the group, or if the operation
|
34
|
+
# to add them succeeds.
|
35
|
+
#
|
36
|
+
def add(new_member)
|
37
|
+
debugger
|
38
|
+
return false unless new_member.is_a?(ActiveDirectory::User) || new_member.is_a?(ActiveDirectory::Group)
|
39
|
+
if @@ldap.modify(:dn => distinguishedName, :operations => [
|
40
|
+
[ :add, :member, new_member.distinguishedName ]
|
41
|
+
])
|
42
|
+
self.reload
|
43
|
+
return true
|
44
|
+
else
|
45
|
+
return has_member?(new_member)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
#
|
50
|
+
# Remove a User or Group from this Group. Returns true if the User or
|
51
|
+
# Group does not belong to this Group, or if the operation to remove them
|
52
|
+
# succeeds.
|
53
|
+
#
|
54
|
+
def remove(member)
|
55
|
+
return false unless member.is_a?(User) || member.is_a?(Group)
|
56
|
+
if @@ldap.modify(:dn => distinguishedName, :operations => [
|
57
|
+
[ :delete, :member, member.distinguishedName ]
|
58
|
+
])
|
59
|
+
self.reload
|
60
|
+
return true
|
61
|
+
else
|
62
|
+
return !has_member?(member)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
#
|
67
|
+
# Return true if members exist in this group.
|
68
|
+
#
|
69
|
+
def has_members?
|
70
|
+
begin
|
71
|
+
return (@entry.member.nil? || @entry.member.empty?) ? false : true
|
72
|
+
rescue NoMethodError
|
73
|
+
return false
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
#
|
78
|
+
# Returns an array of all User objects that belong to this group.
|
79
|
+
#
|
80
|
+
# If the recursive argument is passed as false, then only Users who
|
81
|
+
# belong explicitly to this Group are returned.
|
82
|
+
#
|
83
|
+
# If the recursive argument is passed as true, then all Users who
|
84
|
+
# belong to this Group, or any of its subgroups, are returned.
|
85
|
+
#
|
86
|
+
def member_users(recursive = false)
|
87
|
+
return [] unless has_members?
|
88
|
+
if recursive
|
89
|
+
if @member_users_r.nil?
|
90
|
+
@member_users_r = []
|
91
|
+
@entry.member.each do |member_dn|
|
92
|
+
subuser = User.find_by_distinguishedName(member_dn)
|
93
|
+
if subuser
|
94
|
+
@member_users_r << subuser
|
95
|
+
else
|
96
|
+
subgroup = Group.find_by_distinguishedName(member_dn)
|
97
|
+
if subgroup
|
98
|
+
@member_users_r = @member_users_r.concat(subgroup.member_users(true))
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
return @member_users_r
|
104
|
+
else
|
105
|
+
@member_users_non_r ||= @entry.member.collect { |dn| User.find_by_distinguishedName(dn) }.delete_if { |u| u.nil? }
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
#
|
110
|
+
# Returns an array of all Group objects that belong to this group.
|
111
|
+
#
|
112
|
+
# If the recursive argument is passed as false, then only Groups that
|
113
|
+
# belong explicitly to this Group are returned.
|
114
|
+
#
|
115
|
+
# If the recursive argument is passed as true, then all Groups that
|
116
|
+
# belong to this Group, or any of its subgroups, are returned.
|
117
|
+
#
|
118
|
+
def member_groups(recursive = false)
|
119
|
+
return [] unless has_members?
|
120
|
+
if recursive
|
121
|
+
if @member_groups_r.nil?
|
122
|
+
@member_groups_r = []
|
123
|
+
@entry.member.each do |member_dn|
|
124
|
+
subgroup = Group.find_by_distinguishedName(member_dn)
|
125
|
+
if subgroup
|
126
|
+
@member_groups_r << subgroup
|
127
|
+
@member_groups_r = @member_groups_r.concat(subgroup.member_groups(true))
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
return @member_groups_r
|
132
|
+
else
|
133
|
+
@member_groups_non_r ||= @entry.member.collect { |dn| Group.find_by_distinguishedName(dn) }.delete_if { |g| g.nil? }
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
#
|
138
|
+
# Returns an array of Group objects that this Group belongs to.
|
139
|
+
#
|
140
|
+
def groups
|
141
|
+
return [] if memberOf.nil?
|
142
|
+
@groups ||= memberOf.collect { |group_dn| Group.find_by_distinguishedName(group_dn) }
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module ActiveDirectory
|
2
|
+
module Member
|
3
|
+
#
|
4
|
+
# Returns true if this member (User or Group) is a member of
|
5
|
+
# the passed Group object.
|
6
|
+
#
|
7
|
+
def member_of?(usergroup)
|
8
|
+
group_dns = memberOf
|
9
|
+
return false if group_dns.nil? || group_dns.empty?
|
10
|
+
#group_dns = [group_dns] unless group_dns.is_a?(Array)
|
11
|
+
group_dns.include?(usergroup.dn)
|
12
|
+
end
|
13
|
+
|
14
|
+
#
|
15
|
+
# Add the member to the passed Group object. Returns true if this object
|
16
|
+
# is already a member of the Group, or if the operation to add it succeeded.
|
17
|
+
#
|
18
|
+
def join(group)
|
19
|
+
return false unless group.is_a?(Group)
|
20
|
+
group.add(self)
|
21
|
+
end
|
22
|
+
|
23
|
+
#
|
24
|
+
# Remove the member from the passed Group object. Returns true if this
|
25
|
+
# object is not a member of the Group, or if the operation to remove it
|
26
|
+
# succeeded.
|
27
|
+
#
|
28
|
+
def unjoin(group)
|
29
|
+
return false unless group.is_a?(Group)
|
30
|
+
group.remove(self)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,164 @@
|
|
1
|
+
module ActiveDirectory
|
2
|
+
class OrganizationalUnit < Base
|
3
|
+
include Member
|
4
|
+
|
5
|
+
def self.filter # :nodoc:
|
6
|
+
Net::LDAP::Filter.eq(:objectClass,'organizationalUnit')
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.required_attributes #:nodoc:
|
10
|
+
{ :objectClass => ['top', 'organizationalUnit'] }
|
11
|
+
end
|
12
|
+
|
13
|
+
#
|
14
|
+
# Resets member information.
|
15
|
+
#
|
16
|
+
def reload
|
17
|
+
@member_users_non_r = nil
|
18
|
+
@member_users_r = nil
|
19
|
+
@member_groups_non_r = nil
|
20
|
+
@member_groups_r = nil
|
21
|
+
@groups = nil
|
22
|
+
super
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
module ActiveDirectory
|
28
|
+
class Ou < Base
|
29
|
+
include Member
|
30
|
+
|
31
|
+
def self.filter # :nodoc:
|
32
|
+
Net::LDAP::Filter.eq(:objectClass,'ou')
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.required_attributes # :nodoc:
|
36
|
+
{ :objectClass => [ 'top', 'ou' ] }
|
37
|
+
end
|
38
|
+
|
39
|
+
def reload # :nodoc:
|
40
|
+
@member_users_non_r = nil
|
41
|
+
@member_users_r = nil
|
42
|
+
@member_groups_non_r = nil
|
43
|
+
@member_groups_r = nil
|
44
|
+
@groups = nil
|
45
|
+
super
|
46
|
+
end
|
47
|
+
|
48
|
+
#
|
49
|
+
# Returns true if the passed User or Group object belongs to
|
50
|
+
# this group. For performance reasons, the check is handled
|
51
|
+
# by the User or Group object passed.
|
52
|
+
#
|
53
|
+
def has_member?(user)
|
54
|
+
user.member_of?(self)
|
55
|
+
end
|
56
|
+
|
57
|
+
#
|
58
|
+
# Add the passed User or Group object to this Group. Returns true if
|
59
|
+
# the User or Group is already a member of the group, or if the operation
|
60
|
+
# to add them succeeds.
|
61
|
+
#
|
62
|
+
def add(new_member)
|
63
|
+
debugger
|
64
|
+
return false unless new_member.is_a?(ActiveDirectory::User) || new_member.is_a?(ActiveDirectory::Group)
|
65
|
+
if @@ldap.modify(:dn => distinguishedName, :operations => [
|
66
|
+
[ :add, :member, new_member.distinguishedName ]
|
67
|
+
])
|
68
|
+
self.reload
|
69
|
+
return true
|
70
|
+
else
|
71
|
+
return has_member?(new_member)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
#
|
76
|
+
# Remove a User or Group from this Group. Returns true if the User or
|
77
|
+
# Group does not belong to this Group, or if the operation to remove them
|
78
|
+
# succeeds.
|
79
|
+
#
|
80
|
+
def remove(member)
|
81
|
+
return false unless member.is_a?(User) || member.is_a?(Group)
|
82
|
+
if @@ldap.modify(:dn => distinguishedName, :operations => [
|
83
|
+
[ :delete, :member, member.distinguishedName ]
|
84
|
+
])
|
85
|
+
self.reload
|
86
|
+
return true
|
87
|
+
else
|
88
|
+
return !has_member?(member)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
#
|
93
|
+
# Return true if members exist in this group.
|
94
|
+
#
|
95
|
+
def has_members?
|
96
|
+
begin
|
97
|
+
return (@entry.member.nil? || @entry.member.empty?) ? false : true
|
98
|
+
rescue NoMethodError
|
99
|
+
return false
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
#
|
104
|
+
# Returns an array of all User objects that belong to this group.
|
105
|
+
#
|
106
|
+
# If the recursive argument is passed as false, then only Users who
|
107
|
+
# belong explicitly to this Group are returned.
|
108
|
+
#
|
109
|
+
# If the recursive argument is passed as true, then all Users who
|
110
|
+
# belong to this Group, or any of its subgroups, are returned.
|
111
|
+
#
|
112
|
+
def member_users(recursive = false)
|
113
|
+
return [] unless has_members?
|
114
|
+
if recursive
|
115
|
+
if @member_users_r.nil?
|
116
|
+
@member_users_r = []
|
117
|
+
@entry.member.each do |member_dn|
|
118
|
+
subuser = User.find_by_distinguishedName(member_dn)
|
119
|
+
if subuser
|
120
|
+
@member_users_r << subuser
|
121
|
+
else
|
122
|
+
subgroup = Group.find_by_distinguishedName(member_dn)
|
123
|
+
if subgroup
|
124
|
+
@member_users_r = @member_users_r.concat(subgroup.member_users(true))
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
return @member_users_r
|
130
|
+
else
|
131
|
+
@member_users_non_r ||= @entry.member.collect { |dn| User.find_by_distinguishedName(dn) }.delete_if { |u| u.nil? }
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
#
|
136
|
+
# Returns an array of all Group objects that belong to this group.
|
137
|
+
#
|
138
|
+
# If the recursive argument is passed as false, then only Groups that
|
139
|
+
# belong explicitly to this Group are returned.
|
140
|
+
#
|
141
|
+
# If the recursive argument is passed as true, then all Groups that
|
142
|
+
# belong to this Group, or any of its subgroups, are returned.
|
143
|
+
#
|
144
|
+
def member_groups(recursive = false)
|
145
|
+
return [] unless has_members?
|
146
|
+
if recursive
|
147
|
+
if @member_groups_r.nil?
|
148
|
+
@member_groups_r = []
|
149
|
+
@entry.member.each do |member_dn|
|
150
|
+
subgroup = Group.find_by_distinguishedName(member_dn)
|
151
|
+
if subgroup
|
152
|
+
@member_groups_r << subgroup
|
153
|
+
@member_groups_r = @member_groups_r.concat(subgroup.member_groups(true))
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
return @member_groups_r
|
158
|
+
else
|
159
|
+
@member_groups_non_r ||= @entry.member.collect { |dn| Group.find_by_distinguishedName(dn) }.delete_if { |g| g.nil? }
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
end
|
164
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module ActiveDirectory
|
2
|
+
class Password
|
3
|
+
#
|
4
|
+
# Encodes an unencrypted password into an encrypted password
|
5
|
+
# that the Active Directory server will understand.
|
6
|
+
#
|
7
|
+
def self.encode(password)
|
8
|
+
("\"#{password}\"".split(//).collect { |c| "#{c}\000" }).join
|
9
|
+
end
|
10
|
+
|
11
|
+
#
|
12
|
+
# Always returns nil, since you can't decrypt the User's encrypted
|
13
|
+
# password.
|
14
|
+
#
|
15
|
+
def self.decode(hashed)
|
16
|
+
nil
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|