radum 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +22 -0
- data/lib/radum.rb +10 -0
- data/lib/radum/ad.rb +3355 -0
- data/lib/radum/container.rb +367 -0
- data/lib/radum/group.rb +455 -0
- data/lib/radum/logger.rb +67 -0
- data/lib/radum/user.rb +1087 -0
- data/test/tc_ad.rb +220 -0
- data/test/tc_container.rb +205 -0
- data/test/tc_group.rb +161 -0
- data/test/tc_unix_user.rb +98 -0
- data/test/tc_user.rb +175 -0
- metadata +91 -0
@@ -0,0 +1,367 @@
|
|
1
|
+
module RADUM
|
2
|
+
# The Container class represents a directory entry which contains users and
|
3
|
+
# groups, usually an orgainizational unit (OU).
|
4
|
+
class Container
|
5
|
+
# The String represenation of the Container object's name. The name should
|
6
|
+
# be the LDAP distinguishedName attribute without the AD root path
|
7
|
+
# component.
|
8
|
+
attr_reader :name
|
9
|
+
# The AD object the Container belongs to.
|
10
|
+
attr_reader :directory
|
11
|
+
# The LDAP distinguishedName attribute for this Container.
|
12
|
+
attr_reader :distinguished_name
|
13
|
+
# An Array of User and UNIXUser objects that are in this Container.
|
14
|
+
attr_reader :users
|
15
|
+
# An Array of User and UNIXUser objects set for removal from this Container.
|
16
|
+
attr_reader :removed_users
|
17
|
+
# An Array of Group and UNIXGroup objects that are in this Container.
|
18
|
+
attr_reader :groups
|
19
|
+
# An Array of Group and UNIXGroup objects set for removal from this
|
20
|
+
# Container.
|
21
|
+
attr_reader :removed_groups
|
22
|
+
|
23
|
+
# Create a new Container object that represents an Active Directory
|
24
|
+
# container or organizational unit that contains users and groups. This
|
25
|
+
# method takes a Hash containing arguments, all of which are required.
|
26
|
+
# The supported arguments follow:
|
27
|
+
#
|
28
|
+
# * :name => The relative path in Active Directory [required]
|
29
|
+
# * :directory => The Container object's associated AD [required]
|
30
|
+
#
|
31
|
+
# An example instantiation follows:
|
32
|
+
#
|
33
|
+
# ad = RADUM::AD.new :root => 'dc=example,dc=com',
|
34
|
+
# :user => 'cn=Administrator,cn=Users',
|
35
|
+
# :password => 'password',
|
36
|
+
# :server => '192.168.1.1'
|
37
|
+
# cn = RADUM::Container.new :name => 'ou=People', :directory => ad
|
38
|
+
#
|
39
|
+
# The :name argument specifies the path to the container or organizational
|
40
|
+
# unit in Active Directory equivalent to the LDAP distinguishedName
|
41
|
+
# attribute for container or organizational unit without the root portion.
|
42
|
+
# The :directory argument is the AD object that owns the Container. A
|
43
|
+
# RuntimeError is raised if either of these arguments are missing.
|
44
|
+
#
|
45
|
+
# === Parameter Types
|
46
|
+
#
|
47
|
+
# * :name [String]
|
48
|
+
# * :directory [AD]
|
49
|
+
#
|
50
|
+
# Extraneous spaces are removed from the :name argument. The Container :name
|
51
|
+
# can have spaces, but extra spaces after any "," characters are removed
|
52
|
+
# automatically along with leading and traling white space. The Container
|
53
|
+
# must not already be in the AD or a RuntimeError is raised. Note that you
|
54
|
+
# can create Container objects for an actual container in Active Directory
|
55
|
+
# or an organizational unit (referred to here as a "container" since it
|
56
|
+
# logically contains objects and this is a higher level representation).
|
57
|
+
# Only specify Containers that are really containers ("cn=Foo") or
|
58
|
+
# organizational units ("ou=Foo"). Also note that orgainizational units can
|
59
|
+
# hold containers, but containers cannot hold organizational units.
|
60
|
+
# Therefore "ou=foo,cn=bar" is invalid, but "cn=foo,ou=bar" is valid. A
|
61
|
+
# RuntimeError is raised if this rule is violated. Lastly, Container
|
62
|
+
# objects are in a conceptually flat namespace. In other words,
|
63
|
+
# "cn=foo,ou=bar" is its own Container object. It is not represented as a
|
64
|
+
# child of the "ou=bar" organizational unit. This has been accounted for
|
65
|
+
# when synchronizing with AD#sync so that things work. For example, the
|
66
|
+
# "cn=foo,ou=bar" Container object will cause the "ou=bar" organizational
|
67
|
+
# unit to be created first, if necessary, before the "cn=bar" child
|
68
|
+
# container is created.
|
69
|
+
def initialize(args = {})
|
70
|
+
@name = args[:name] or raise "Container :name argument required."
|
71
|
+
@name.gsub!(/,\s+/, ",")
|
72
|
+
@name.strip!
|
73
|
+
|
74
|
+
if @name =~ /[Oo][Uu]=.*[Cc][Nn]=/
|
75
|
+
raise "Container CN objects cannot contain OU objects."
|
76
|
+
end
|
77
|
+
|
78
|
+
@directory = args[:directory] or raise "Container :directory argument" +
|
79
|
+
" required."
|
80
|
+
|
81
|
+
# The container name (like a user) must be unique (case-insensitive).
|
82
|
+
# We would not want someone accidently making two equal containers
|
83
|
+
# and adding users/groups in the wrong way.
|
84
|
+
if @directory.find_container(name)
|
85
|
+
raise "Container is already in the directory."
|
86
|
+
end
|
87
|
+
|
88
|
+
@distinguished_name = @name + "," + @directory.root
|
89
|
+
# This has to be set first before adding the Container to the AD.
|
90
|
+
@removed = false
|
91
|
+
@directory.add_container self
|
92
|
+
@users = []
|
93
|
+
@removed_users = []
|
94
|
+
@groups = []
|
95
|
+
@removed_groups = []
|
96
|
+
end
|
97
|
+
|
98
|
+
# Add User and UNIXUser objects to the Container. User and UNIXUser objects
|
99
|
+
# that were removed or destroyed cannot be added back again and are ignored.
|
100
|
+
# The User or UNIXUser must have the Container as its container attribute
|
101
|
+
# or a RuntimeError is raised.
|
102
|
+
#
|
103
|
+
# === Parameter Types
|
104
|
+
#
|
105
|
+
# * user [User or UNIXUser]
|
106
|
+
def add_user(user)
|
107
|
+
unless user.removed?
|
108
|
+
if self == user.container
|
109
|
+
# We don't want to add a user more than once.
|
110
|
+
unless @users.include?(user)
|
111
|
+
@users.push user
|
112
|
+
@removed_users.delete user
|
113
|
+
@directory.rids.push user.rid if user.rid
|
114
|
+
@directory.uids.push user.uid if user.instance_of?(UNIXUser)
|
115
|
+
end
|
116
|
+
else
|
117
|
+
raise "User must be in this container."
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
# Remove a User or UNIXUser object from the Container. This sets the
|
123
|
+
# User or UNIXUser object's removed attribute to true. If the User or
|
124
|
+
# UNIXUser is removed from the Container, it is effectively deleted
|
125
|
+
# from Active Directory. Any Group or UNIXGroup objects the User or UNIXUser
|
126
|
+
# belongs to will have their membership removed. This means that the
|
127
|
+
# User or UNIXUser will have their Group or UNIXGroup memberships
|
128
|
+
# removed for each Group or UNIXGroup they were in as well. The User or
|
129
|
+
# UNIXUser cannot be added back after removed, but it will only be
|
130
|
+
# removed from Active Directory after AD#sync is called. Any references
|
131
|
+
# to the User or UNIXUser should be discarded. The User or UNIXUser must
|
132
|
+
# be in the Container or a RuntimeError is raised. Already removed User
|
133
|
+
# or UNIXUser objects are ignored.
|
134
|
+
#
|
135
|
+
# === Parameter Types
|
136
|
+
#
|
137
|
+
# * user [User or UNIXUser]
|
138
|
+
def remove_user(user)
|
139
|
+
return if user.removed?
|
140
|
+
if self == user.container
|
141
|
+
@users.delete user
|
142
|
+
@directory.rids.delete user.rid if user.rid
|
143
|
+
@directory.uids.delete user.uid if user.instance_of?(UNIXUser)
|
144
|
+
|
145
|
+
@directory.groups.each do |group|
|
146
|
+
if group.users.include?(user)
|
147
|
+
if user.instance_of?(UNIXUser)
|
148
|
+
group.remove_user user unless group == user.unix_main_group
|
149
|
+
else
|
150
|
+
group.remove_user user
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
user.set_removed
|
156
|
+
|
157
|
+
# We have to remove the user first before we can remove the user's
|
158
|
+
# membership in their unix_main_group. It is safe to attempt removing
|
159
|
+
# a user from their unix_main_group if they are already removed.
|
160
|
+
if user.instance_of?(UNIXUser)
|
161
|
+
user.unix_main_group.remove_user user
|
162
|
+
end
|
163
|
+
|
164
|
+
@removed_users.push user unless @removed_users.include?(user)
|
165
|
+
else
|
166
|
+
raise "User must be in this container."
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
# Destroy a reference to the User or UNIXUser. This removes any reference
|
171
|
+
# to the User or UNIXUser from the RADUM system. This is different from
|
172
|
+
# removing a User or UNIXUser. Removal causes the User or UNIXUser to be
|
173
|
+
# deleted from Active Directory. Destroying the User or UNIXUser does not
|
174
|
+
# cause the User or UNIXUser to be removed from Active Directory, but
|
175
|
+
# it does remove all references to the User or UNIXUser from the system.
|
176
|
+
# The User or UNIXUser must be in the Container or a RuntimeError is
|
177
|
+
# raised. This does set the User or UNIXUser object's removed attribute
|
178
|
+
# to true and any references to the User or UNIXUser should be discarded.
|
179
|
+
# Once a User or UNIXUser is destroyed it cannot be added back to the
|
180
|
+
# Container.
|
181
|
+
#
|
182
|
+
# === Parameter Types
|
183
|
+
#
|
184
|
+
# * user [User or UNIXUser]
|
185
|
+
def destroy_user(user)
|
186
|
+
# Note you have to allow destruction of users even if they have been
|
187
|
+
# removed because this is the only way removed users are really deleted
|
188
|
+
# from the RADUM environment once they are deleted from Active Directory.
|
189
|
+
if self == user.container
|
190
|
+
@users.delete user
|
191
|
+
@removed_users.delete user
|
192
|
+
@directory.rids.delete user.rid if user.rid
|
193
|
+
@directory.uids.delete user.uid if user.instance_of?(UNIXUser)
|
194
|
+
|
195
|
+
@directory.groups.each do |group|
|
196
|
+
group.destroy_user user if group.users.include?(user)
|
197
|
+
end
|
198
|
+
|
199
|
+
user.set_removed
|
200
|
+
else
|
201
|
+
raise "User must be in this container."
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
# Add Group and UNIXGroup objects to the Container. Group and UNIXGroup
|
206
|
+
# objects that were removed or destroyed cannot be added back again and are
|
207
|
+
# ignored. The Group or UNIXGroup must have the Container as its container
|
208
|
+
# attribute or a RuntimeError is raised.
|
209
|
+
#
|
210
|
+
# === Parameter Types
|
211
|
+
#
|
212
|
+
# * group [Group or UNIXGroup]
|
213
|
+
def add_group(group)
|
214
|
+
unless group.removed?
|
215
|
+
if self == group.container
|
216
|
+
# We don't want to add a group more than once.
|
217
|
+
unless @groups.include?(group)
|
218
|
+
@groups.push group
|
219
|
+
@removed_groups.delete group
|
220
|
+
@directory.rids.push group.rid if group.rid
|
221
|
+
@directory.gids.push group.gid if group.instance_of?(UNIXGroup)
|
222
|
+
end
|
223
|
+
else
|
224
|
+
raise "Group must be in this container."
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
# Remove a Group or UNIXGroup object from the Container. This sets the
|
230
|
+
# Group or UNIXGroup object's removed attribute to true. A Group or
|
231
|
+
# UNIXGroup cannot be removed if it is still any User object's primary
|
232
|
+
# Windows group. A UNIXGroup cannot be removed if it is any User object's
|
233
|
+
# UNIX main group. In both cases, a RuntimeError will be raised. If the
|
234
|
+
# Group or UNIXGroup is removed from the Container, it is effectively
|
235
|
+
# deleted from Active Directory. Any Group or UNIXGroup objects the Group or
|
236
|
+
# UNIXGroup belongs to will have their membership removed. This
|
237
|
+
# means that the Group or UNIXGroup will have their Group or UNIXGroup
|
238
|
+
# memberships removed for each Group or UNIXGroup they were in as well.
|
239
|
+
# The Group or UNIXGroup cannot be added back after removed, but it will
|
240
|
+
# only be removed from Active Directory after AD#sync is called. Any
|
241
|
+
# references to the Group or UNIXGroup should be discarded. The Group or
|
242
|
+
# UNIXGroup must be in the Container or a RuntimeError is raised. Already
|
243
|
+
# removed Group or UNIXGroup objects are ignored.
|
244
|
+
#
|
245
|
+
# There are further checks in Active Directory to make sure the Group
|
246
|
+
# or UNIXGroup can really be removed. A warning is displayed if the Group
|
247
|
+
# or UNIXGroup cannot be removed in the AD#sync call. In this method,
|
248
|
+
# RADUM only checks objects it knows about at the time of the call.
|
249
|
+
#
|
250
|
+
# === Parameter Types
|
251
|
+
#
|
252
|
+
# * group [Group or UNIXGroup]
|
253
|
+
def remove_group(group)
|
254
|
+
return if group.removed?
|
255
|
+
if self == group.container
|
256
|
+
# We cannot remove a group that still has a user referencing it as
|
257
|
+
# their primary_group or unix_main_group.
|
258
|
+
@directory.users.each do |user|
|
259
|
+
if group == user.primary_group
|
260
|
+
raise "Cannot remove group #{group.name}: it is " +
|
261
|
+
"#{user.username}'s primary Windows group."
|
262
|
+
end
|
263
|
+
|
264
|
+
if user.instance_of?(UNIXUser)
|
265
|
+
if group == user.unix_main_group
|
266
|
+
raise "Cannot remove group #{group.name}: it is " +
|
267
|
+
"#{user.username}'s UNIX main group."
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
@groups.delete group
|
273
|
+
@directory.rids.delete group.rid if group.rid
|
274
|
+
@directory.gids.delete group.gid if group.instance_of?(UNIXGroup)
|
275
|
+
|
276
|
+
@directory.groups.each do |current_group|
|
277
|
+
if current_group.groups.include?(group)
|
278
|
+
current_group.remove_group group
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
@directory.users.each do |user|
|
283
|
+
if user.groups.include?(group)
|
284
|
+
user.remove_group group
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
group.set_removed
|
289
|
+
@removed_groups.push group unless @removed_groups.include?(group)
|
290
|
+
else
|
291
|
+
raise "Group must be in this container."
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
# Destroy a reference to the Group or UNIXGroup. This removes any reference
|
296
|
+
# to the Group or UNIXGroup from the RADUM system. This is different from
|
297
|
+
# removing a Group or UNIXGroup. Removal causes the Group or UNIXGroup to be
|
298
|
+
# deleted from Active Directory. Destroying the Group or UNIXGroup does not
|
299
|
+
# cause the Group or UNIXGroup to be removed from Active Directory, but
|
300
|
+
# it does remove all references to the Group or UNIXGroup from the system.
|
301
|
+
# The Group or UNIXGroup must be in the Container or a RuntimeError is
|
302
|
+
# raised. This does set the Group or UNIXGroup object's removed attribute
|
303
|
+
# to true and any references to the Group or UNIXGroup should be discarded.
|
304
|
+
# Once a Group or UNIXGroup is destroyed it cannot be added back to the
|
305
|
+
# Container.
|
306
|
+
#
|
307
|
+
# === Parameter Types
|
308
|
+
#
|
309
|
+
# * group [Group or UNIXGroup]
|
310
|
+
def destroy_group(group)
|
311
|
+
# Note you have to allow destruction of groups even if they have been
|
312
|
+
# removed because this is the only way removed groups are really deleted
|
313
|
+
# from the RADUM environment once they are deleted from Active Directory.
|
314
|
+
if self == group.container
|
315
|
+
# We cannot destroy a group that still has a user referencing it as
|
316
|
+
# their primary_group or unix_main_group.
|
317
|
+
@directory.users.each do |user|
|
318
|
+
if group == user.primary_group
|
319
|
+
raise "Cannot destroy group #{group.name}: it is " +
|
320
|
+
"#{user.username}'s primary Windows group."
|
321
|
+
end
|
322
|
+
|
323
|
+
if user.instance_of?(UNIXUser)
|
324
|
+
if group == user.unix_main_group
|
325
|
+
raise "Cannot destroy group #{group.name}: it is " +
|
326
|
+
"#{user.username}'s UNIX main group."
|
327
|
+
end
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
@groups.delete group
|
332
|
+
@removed_groups.delete group
|
333
|
+
@directory.rids.delete group.rid if group.rid
|
334
|
+
@directory.gids.delete group.gid if group.instance_of?(UNIXGroup)
|
335
|
+
|
336
|
+
@directory.groups.each do |current_group|
|
337
|
+
if current_group.groups.include?(group)
|
338
|
+
current_group.destroy_group group
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
342
|
+
@directory.users.each do |user|
|
343
|
+
user.destroy_group group
|
344
|
+
end
|
345
|
+
|
346
|
+
group.set_removed
|
347
|
+
else
|
348
|
+
raise "Group must be in this container."
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
# True if the Container has been removed from its AD, false otherwise.
|
353
|
+
def removed?
|
354
|
+
@removed
|
355
|
+
end
|
356
|
+
|
357
|
+
# Set the Container removed flag.
|
358
|
+
def set_removed # :nodoc:
|
359
|
+
@removed = true
|
360
|
+
end
|
361
|
+
|
362
|
+
# The String representation of the Container object.
|
363
|
+
def to_s
|
364
|
+
"Container [#{@name},#{@directory.root}]"
|
365
|
+
end
|
366
|
+
end
|
367
|
+
end
|
data/lib/radum/group.rb
ADDED
@@ -0,0 +1,455 @@
|
|
1
|
+
module RADUM
|
2
|
+
# The Group class represents a standard Windows group.
|
3
|
+
class Group
|
4
|
+
# The String representation of the Group or UNIXGroup name. This is similar
|
5
|
+
# to a User or UNIXUser username in that it does not contain any LDAP path
|
6
|
+
# components. This corresponds to the LDAP cn, msSFU30Name, name, and
|
7
|
+
# sAMAccountName attributes.
|
8
|
+
attr_reader :name
|
9
|
+
# The Container object the Group or UNIXGroup belongs to.
|
10
|
+
attr_reader :container
|
11
|
+
# The RADUM group type of the Group or UNIXGroup. This corresponds to the
|
12
|
+
# LDAP groupType attribute. This defaults to GROUP_GLOBAL_SECURITY when
|
13
|
+
# a Group or UNIXGroup is created using Group.new or UNIXGroup.new, but
|
14
|
+
# it is set to the correct value when a Group or UNIXGroup is loaded by
|
15
|
+
# AD#load from the AD object the Container belongs to.
|
16
|
+
attr_reader :type
|
17
|
+
# The RID of the Group or UNIXGroup object. This correponds to part of the
|
18
|
+
# LDAP objectSid attribute. This is set when the Group or UNIXGroup is
|
19
|
+
# loaded by AD#load from the AD object the Container belongs to. This
|
20
|
+
# attribute should not be specified in the Group.new or UNIXGroup.new
|
21
|
+
# methods when creating a new Group or UNIXGroup by hand.
|
22
|
+
attr_reader :rid
|
23
|
+
# The LDAP distinguishedName attribute for this Group or UNIXGroup.
|
24
|
+
attr_reader :distinguished_name
|
25
|
+
# The User or UNIXUser objects the Group or UNIXGroup has a members. User
|
26
|
+
# and UNIXUser objects are implicit members of their primary_group as well,
|
27
|
+
# but they are not added to the users array directly. This matches the
|
28
|
+
# implicit membership in the primary Windows group in Active Directory.
|
29
|
+
attr_reader :users
|
30
|
+
# An array of User or UNIXUser objects removed from the Group or UNIXGroup.
|
31
|
+
attr_reader :removed_users
|
32
|
+
# The Group or UNIXGroup objects that are members of the Group or UNIXGroup.
|
33
|
+
attr_reader :groups
|
34
|
+
# An array of Group or UNIXGroup objects removed from the Group or
|
35
|
+
# UNIXGroup.
|
36
|
+
attr_reader :removed_groups
|
37
|
+
|
38
|
+
# Create a new Group object that represents a Windows group in Active
|
39
|
+
# Directory. This method takes a Hash containing arguments, some of which
|
40
|
+
# are required and others optional. The supported arguments follow:
|
41
|
+
#
|
42
|
+
# * :name => The Group object's name [required]
|
43
|
+
# * :container => The Group object's associated Container [required]
|
44
|
+
# * :type => The RADUM group type [default GROUP_GLOBAL_SECURITY]
|
45
|
+
# * :rid => The RID of the Group object [optional]
|
46
|
+
#
|
47
|
+
# The :name argument (case-insensitive) and the :rid argument must be
|
48
|
+
# unique in the AD object, otherwise a RuntimeError is raised. The :name
|
49
|
+
# argument has leading and trailing white space removed. The :type
|
50
|
+
# argument must be one of the RADUM group type constants. The :rid argument
|
51
|
+
# should not be set directly except from the AD#load method itself.
|
52
|
+
# The Group object automatically adds itself to the Container object
|
53
|
+
# specified by the :container argument.
|
54
|
+
#
|
55
|
+
# === Parameter Types
|
56
|
+
#
|
57
|
+
# * :name [String]
|
58
|
+
# * :container [Container]
|
59
|
+
# * :type [integer => RADUM group type constant]
|
60
|
+
# * :rid [integer]
|
61
|
+
def initialize(args = {})
|
62
|
+
@rid = args[:rid] || nil
|
63
|
+
@container = args[:container] or raise "Group :container argument" +
|
64
|
+
" required."
|
65
|
+
|
66
|
+
# The RID must be unique.
|
67
|
+
if @container.directory.rids.include?(@rid)
|
68
|
+
raise "RID #{rid} is already in use in the directory."
|
69
|
+
end
|
70
|
+
|
71
|
+
@name = args[:name] or raise "Group :name argument required."
|
72
|
+
@name.strip!
|
73
|
+
|
74
|
+
# The group name (like a user) must be unique (case-insensitive). This
|
75
|
+
# is needed in case someone tries to make the same group name in two
|
76
|
+
# different containers.
|
77
|
+
if @container.directory.find_group_by_name(@name)
|
78
|
+
raise "Group is already in the directory."
|
79
|
+
end
|
80
|
+
|
81
|
+
@type = args[:type] || GROUP_GLOBAL_SECURITY
|
82
|
+
@distinguished_name = "cn=" + name + "," + @container.name + "," +
|
83
|
+
@container.directory.root
|
84
|
+
@users = []
|
85
|
+
@removed_users = []
|
86
|
+
@groups = []
|
87
|
+
@removed_groups = []
|
88
|
+
# This has to be set first before adding the Group to the Container. This
|
89
|
+
# is delayed for a UNIXGroup because it needs the rest of its attributes
|
90
|
+
# set before adding to the Container.
|
91
|
+
@removed = false
|
92
|
+
@container.add_group self unless instance_of?(UNIXGroup)
|
93
|
+
@modified = true
|
94
|
+
@loaded = false
|
95
|
+
end
|
96
|
+
|
97
|
+
# Make the User or UNIXUser a member of the Group or UNIXGroup. This
|
98
|
+
# represents the LDAP member attribute for the Group or UNIXGroup. A User
|
99
|
+
# or UNIXUser is listed in the Group or UNIXGroup object's LDAP member
|
100
|
+
# attribute unless it is their primary_group. In that case, the User or
|
101
|
+
# UNIXUser object's LDAP primaryGroupID attribute is used (which contains
|
102
|
+
# the RID of that Group or UNIXGroup - the Group or UNIXGroup does not list
|
103
|
+
# the User or UNIXUser in its LDAP member attribute, hence the logic in the
|
104
|
+
# code). The unix_main_group for UNIXUsers has the UNIXUser as a member in a
|
105
|
+
# similar way based on the LDAP gidNumber attribute for the UNIXUser. The
|
106
|
+
# UNIXGroup object's LDAP memberUid and msSFU30PosixMember attributes do
|
107
|
+
# not list the UNIXUser as a member of the UNIXGroup is their
|
108
|
+
# unix_main_group, but this module makes sure the UNIXUsers are also
|
109
|
+
# members of their unix_main_group from the Windows perspective. A
|
110
|
+
# RuntimeError is raised if the User or UNIXUser already has this Group or
|
111
|
+
# UNIXGroup as their primary_group or if the Group or UNIXGroup is not in
|
112
|
+
# the same AD object. A RuntimeError is raised if the User or UNIXUser has
|
113
|
+
# been removed.
|
114
|
+
#
|
115
|
+
# This automatically adds the Group or UNIXGroup to the User or UNIXUser
|
116
|
+
# object's list of groups.
|
117
|
+
#
|
118
|
+
# === Parameter Types
|
119
|
+
#
|
120
|
+
# * user [User or UNIXUser]
|
121
|
+
def add_user(user)
|
122
|
+
if user.removed?
|
123
|
+
raise "Cannot add a removed user."
|
124
|
+
end
|
125
|
+
|
126
|
+
if @container.directory == user.container.directory
|
127
|
+
unless self == user.primary_group
|
128
|
+
@users.push user unless @users.include?(user)
|
129
|
+
@removed_users.delete user
|
130
|
+
user.add_group self unless user.groups.include?(self)
|
131
|
+
@modified = true
|
132
|
+
else
|
133
|
+
raise "Group is already the user's primary_group."
|
134
|
+
end
|
135
|
+
else
|
136
|
+
raise "User must be in the same directory."
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# Remove the User or UNIXUser membership in the Group. This automatically
|
141
|
+
# removes the Group from the User or UNIXUser object's list of groups.
|
142
|
+
# A RuntimeError is raised if the User or UNIXUser has been removed.
|
143
|
+
# Any external references to the User or UNIXUser should be discarded.
|
144
|
+
#
|
145
|
+
# === Parameter Types
|
146
|
+
#
|
147
|
+
# * user [User or UNIXUser]
|
148
|
+
def remove_user(user)
|
149
|
+
if user.removed?
|
150
|
+
raise "Cannot remove a removed user."
|
151
|
+
end
|
152
|
+
|
153
|
+
@users.delete user
|
154
|
+
@removed_users.push user unless @removed_users.include?(user)
|
155
|
+
user.remove_group self if user.groups.include?(self)
|
156
|
+
@modified = true
|
157
|
+
end
|
158
|
+
|
159
|
+
# Delete all references to a User or UNIXUser in this object. This should
|
160
|
+
# only be called from Container#destroy_user. The Group or UNIXGroup is not
|
161
|
+
# considered to be modified at this point. It is simply forgetting about the
|
162
|
+
# User or UNIXUser.
|
163
|
+
#
|
164
|
+
# === Parameter Types
|
165
|
+
#
|
166
|
+
# * user [User or UNIXUser]
|
167
|
+
def destroy_user(user) # :nodoc:
|
168
|
+
@users.delete user
|
169
|
+
@removed_users.delete user
|
170
|
+
end
|
171
|
+
|
172
|
+
# Determine if this Group or UNIXGroup is a member of the Group or
|
173
|
+
# UNIXGroup given as the argument.
|
174
|
+
#
|
175
|
+
# === Parameter Types
|
176
|
+
#
|
177
|
+
# * group [Group or UNIXGroup]
|
178
|
+
def member_of?(group)
|
179
|
+
# Memberships are already removed from removed groups. We have to check
|
180
|
+
# the group passed in since groups are only tracking things they contain.
|
181
|
+
# This method for User and UNIXUser objects can just check their own
|
182
|
+
# groups array, but that's not possible here obviously.
|
183
|
+
group.groups.include? self
|
184
|
+
end
|
185
|
+
|
186
|
+
# Make the Group or UNIXGroup given as the argument a member of this Group
|
187
|
+
# or UNIXGroup. This represents the LDAP member attribute for the Group or
|
188
|
+
# UNIXGroup. A RuntimeError is raised if the Group or UNIXGroup is the same
|
189
|
+
# as the current Group or UNIXGroup (cannot be a member of itself) or the
|
190
|
+
# Group or UNIXGroup is not in the same AD object. A RuntimeError is raised
|
191
|
+
# if the Group or UNIXGroup has been removed.
|
192
|
+
#
|
193
|
+
# === Parameter Types
|
194
|
+
#
|
195
|
+
# * group [Group or UNIXGroup]
|
196
|
+
def add_group(group)
|
197
|
+
if group.removed?
|
198
|
+
raise "Cannot add a removed group."
|
199
|
+
end
|
200
|
+
|
201
|
+
unless @container.directory == group.container.directory
|
202
|
+
raise "Group must be in the same directory."
|
203
|
+
end
|
204
|
+
|
205
|
+
if self == group
|
206
|
+
raise "A group cannot have itself as a member."
|
207
|
+
end
|
208
|
+
|
209
|
+
@groups.push group unless @groups.include?(group)
|
210
|
+
@removed_groups.delete group
|
211
|
+
@modified = true
|
212
|
+
end
|
213
|
+
|
214
|
+
# Remove the Group or UNIXGroup membership in the Group or UNIXGroup.
|
215
|
+
# A RuntimeError is raised if the Group or UNIXGroup has been removed.
|
216
|
+
# Any external references to the Group or UNIXGroup should be discarded.
|
217
|
+
#
|
218
|
+
# === Parameter Types
|
219
|
+
#
|
220
|
+
# * group [Group or UNIXGroup]
|
221
|
+
def remove_group(group)
|
222
|
+
if group.removed?
|
223
|
+
raise "Cannot remove a removed group."
|
224
|
+
end
|
225
|
+
|
226
|
+
@groups.delete group
|
227
|
+
@removed_groups.push group unless @removed_groups.include?(group)
|
228
|
+
@modified = true
|
229
|
+
end
|
230
|
+
|
231
|
+
# Delete all references to a Group or UNIXGroup in this object. This should
|
232
|
+
# only be called from Container#destroy_group. The Group or UNIXGroup is not
|
233
|
+
# considered to be modified at this point. It is simply forgetting about the
|
234
|
+
# Group or UNIXGroup.
|
235
|
+
#
|
236
|
+
# === Parameter Types
|
237
|
+
#
|
238
|
+
# * group [Group or UNIXGroup]
|
239
|
+
def destroy_group(group) # :nodoc:
|
240
|
+
@groups.delete group
|
241
|
+
@removed_groups.delete group
|
242
|
+
end
|
243
|
+
|
244
|
+
# Set the loaded flag. This also clears the modified flag. This should only
|
245
|
+
# be called from AD#load and AD#sync unless you really know what you are
|
246
|
+
# doing.
|
247
|
+
def set_loaded # :nodoc:
|
248
|
+
# This allows the modified attribute to be hidden.
|
249
|
+
@loaded = true
|
250
|
+
@modified = false
|
251
|
+
end
|
252
|
+
|
253
|
+
# Check if the Group or UNIXGroup was loaded from Active Directory.
|
254
|
+
def loaded?
|
255
|
+
@loaded
|
256
|
+
end
|
257
|
+
|
258
|
+
# True if the Group or UNIXGroup has been modified. This is true for
|
259
|
+
# manually created Group or UNIXGroup objects and false for initially
|
260
|
+
# loaded Group and UNIXGroup objects.
|
261
|
+
def modified?
|
262
|
+
@modified
|
263
|
+
end
|
264
|
+
|
265
|
+
# Set the RID only if it has not already been set. This is used by the AD
|
266
|
+
# class when doing synchronization. Once there is a RID value, it can be
|
267
|
+
# set. This is not meant for general use. It will only set the rid attribute
|
268
|
+
# if it has not already been set.
|
269
|
+
#
|
270
|
+
# === Parameter Types
|
271
|
+
#
|
272
|
+
# * rid [integer]
|
273
|
+
def set_rid(rid) # :nodoc:
|
274
|
+
if @rid.nil?
|
275
|
+
@rid = rid
|
276
|
+
@container.directory.rids.push rid
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
# True if the Group or UNIXGroup has been removed from its Container, false
|
281
|
+
# otherwise.
|
282
|
+
def removed?
|
283
|
+
@removed
|
284
|
+
end
|
285
|
+
|
286
|
+
# Set the Group or UNIXGroup removed flag.
|
287
|
+
def set_removed # :nodoc:
|
288
|
+
@removed = true
|
289
|
+
end
|
290
|
+
|
291
|
+
# The String representation of the Group object.
|
292
|
+
def to_s
|
293
|
+
"Group [(" + RADUM.group_type_to_s(@type) +
|
294
|
+
", RID #{@rid}) #{@distinguished_name}]"
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
# The UNIXGroup class represents a UNIX Windows group. It is a subclass of
|
299
|
+
# the Group class. See the Group class documentation for its attributes and
|
300
|
+
# methods as well.
|
301
|
+
class UNIXGroup < Group
|
302
|
+
# The UNIXGroup UNIX GID. This corresponds to the LDAP gidNumber
|
303
|
+
# attribute.
|
304
|
+
attr_reader :gid
|
305
|
+
|
306
|
+
# Create a new UNIXGroup object that represents a UNIX group in Active
|
307
|
+
# Directory. A UNIX group is a Windows group that also has UNIX attributes.
|
308
|
+
# This method takes a Hash containing arguments, some of which are required
|
309
|
+
# and others optional. The supported arguments follow:
|
310
|
+
#
|
311
|
+
# * :name => The UNIXGroup object's name [required]
|
312
|
+
# * :container => The UNIXGroup object's associated Container [required]
|
313
|
+
# * :type => The RADUM group type [default GROUP_GLOBAL_SECURITY]
|
314
|
+
# * :rid => The RID of the UNIXGroup object [optional]
|
315
|
+
# * :gid => The UNIXGroup GID attribute [required]
|
316
|
+
# * :nis_domain => The UNIXGroup NIS domain attribute [default "radum"]
|
317
|
+
#
|
318
|
+
# The :name argument (case-insensitive) and the :rid argument must be
|
319
|
+
# unique in the AD object, otherwise a RuntimeError is raised. The :type
|
320
|
+
# argument must be one of the RADUM group type constants. The :rid argument
|
321
|
+
# should not be set directly except from the AD#load method itself.
|
322
|
+
# The UNIXGroup object automatically adds itself to the Container object
|
323
|
+
# specified by the :container argument. The :gid argument specifies the
|
324
|
+
# UNIX GID value of the UNIXGroup. The :gid value must be unique in the
|
325
|
+
# AD object or a RuntimeError is raised (this is an Active Directory
|
326
|
+
# restriction - in UNIX it is fine). The :nis_domain defaults to
|
327
|
+
# "radum". The use of an NIS domain is not strictly required as one could
|
328
|
+
# simply set the right attributes in Active Directory and use LDAP on
|
329
|
+
# clients to access that data, but specifying an NIS domain allows for easy
|
330
|
+
# editing of UNIX attributes using the GUI tools in Windows, thus the use
|
331
|
+
# of a default value.
|
332
|
+
#
|
333
|
+
# Be careful with the :gid argument. RADUM only checks in the AD object
|
334
|
+
# that the :container belongs to, which is the AD object the UNIXGroup
|
335
|
+
# belongs to as well. This does not include any GIDs for other objects in
|
336
|
+
# Active Directory. Creating a UNIXGroup with a duplicate GID will actually
|
337
|
+
# succeed when attempted in LDAP, but the GUI tools in Windows complain. If
|
338
|
+
# you need a new GID value, use AD#load_next_gid to get one as it does check
|
339
|
+
# all GIDs (those RADUM knows about and those in Active Directory). Creating
|
340
|
+
# a UNIXGroup object can't fail if the GID only exists in Active Directory
|
341
|
+
# because AD#load must be able to create UNIXGroup objects that already
|
342
|
+
# exist in Active Directory.
|
343
|
+
#
|
344
|
+
# === Parameter Types
|
345
|
+
#
|
346
|
+
# * :name [String]
|
347
|
+
# * :container [Container]
|
348
|
+
# * :type [integer => RADUM group type constant]
|
349
|
+
# * :rid [integer]
|
350
|
+
# * :gid [integer]
|
351
|
+
# * :nis_domain [String]
|
352
|
+
def initialize(args = {})
|
353
|
+
super args
|
354
|
+
@gid = args[:gid] or raise "UNIXGroup :gid argument required."
|
355
|
+
|
356
|
+
# The GID must be unique. This is an Active Directory restriction.
|
357
|
+
if @container.directory.gids.include?(@gid)
|
358
|
+
raise "GID #{gid} is already in use in the directory."
|
359
|
+
end
|
360
|
+
|
361
|
+
@nis_domain = args[:nis_domain] || "radum"
|
362
|
+
@unix_password = "*"
|
363
|
+
@container.add_group self
|
364
|
+
end
|
365
|
+
|
366
|
+
# Remove the User or UNIXUser membership in the UNIXGroup. This
|
367
|
+
# automatically removes the UNIXGroup from the User or UNIXUser object's
|
368
|
+
# list of groups. This method returns a RuntimeError if the user
|
369
|
+
# has this UNIXGroup as their UNIX main group unless this group is also
|
370
|
+
# the User or UNIXUser object's primary Windows group as well (due to
|
371
|
+
# implicit membership handling, but nothing happens in that case with
|
372
|
+
# respect to UNIX membership). UNIXGroup membership cannot be removed
|
373
|
+
# for the UNIXUser object's UNIX main group because RADUM enforces
|
374
|
+
# Windows group membership in the UNIX main group, unless the group
|
375
|
+
# is also the UNIXUser object's primary Windows group too.
|
376
|
+
#
|
377
|
+
# === Parameter Types
|
378
|
+
#
|
379
|
+
# * user [User or UNIXUser]
|
380
|
+
def remove_user(user)
|
381
|
+
if !user.removed? && user.instance_of?(UNIXUser) &&
|
382
|
+
self == user.unix_main_group && self != user.primary_group
|
383
|
+
raise "A UNIXUser cannot be removed from their unix_main_group."
|
384
|
+
end
|
385
|
+
|
386
|
+
# Removing a user from its unix_main_group is a special case due to
|
387
|
+
# the complicated logic. When called from Container#remove_user the
|
388
|
+
# user's removed flag is set to true when a removal from the user's
|
389
|
+
# unix_main_group is attempted. This catches that special case and
|
390
|
+
# does the right thing. This is needed because of all my checks for
|
391
|
+
# not working with removed objects.
|
392
|
+
if user.removed? && user.instance_of?(UNIXUser) &&
|
393
|
+
self == user.unix_main_group
|
394
|
+
@users.delete user
|
395
|
+
|
396
|
+
# The UNIXUser is not a Windows member of their UNIX main group
|
397
|
+
# directly if it is also their primary Windows group.
|
398
|
+
if self != user.primary_group
|
399
|
+
@removed_users.push user unless @removed_users.include?(user)
|
400
|
+
end
|
401
|
+
|
402
|
+
user.remove_group self if user.groups.include?(self)
|
403
|
+
@modified = true
|
404
|
+
else
|
405
|
+
super user
|
406
|
+
end
|
407
|
+
end
|
408
|
+
|
409
|
+
# The UNIXGroup UNIX NIS domain.
|
410
|
+
def nis_domain
|
411
|
+
@nis_domain
|
412
|
+
end
|
413
|
+
|
414
|
+
# Set the UNIXGroup UNIX NIS domain. This corresponds to the LDAP
|
415
|
+
# msSFU30NisDomain attribute. This needs to be set even if NIS services
|
416
|
+
# are not being used. This defaults to "radum" when a UNIXGroup is created
|
417
|
+
# using UNIXGroup.new, but it is set to the correct value when the UNIXGroup
|
418
|
+
# is loaded by AD#load from the AD object the Container belongs to.
|
419
|
+
#
|
420
|
+
# === Parameter Types
|
421
|
+
#
|
422
|
+
# * nis_domain [String]
|
423
|
+
def nis_domain=(nis_domain)
|
424
|
+
@nis_domain = nis_domain
|
425
|
+
@modified = true
|
426
|
+
end
|
427
|
+
|
428
|
+
# The UNIXGroup UNIX password field.
|
429
|
+
def unix_password
|
430
|
+
@unix_password
|
431
|
+
end
|
432
|
+
|
433
|
+
# Set the UNIXGroup UNIX password field. This can be a crypt or MD5 value
|
434
|
+
# (or whatever your system supports potentially - Windows works with crypt
|
435
|
+
# and MD5 in Microsoft Identity Management for UNIX). This corresponds to
|
436
|
+
# the LDAP unixUserPassword attribute. The unix_password value defaults
|
437
|
+
# to "*" when a UNIXGroup is created using UNIXGroup.new, but it is set
|
438
|
+
# to the correct value when the UNIXGroup is loaded by AD#load from the AD
|
439
|
+
# object the Container belongs to.
|
440
|
+
#
|
441
|
+
# === Parameter Types
|
442
|
+
#
|
443
|
+
# * unix_password [String]
|
444
|
+
def unix_password=(unix_password)
|
445
|
+
@unix_password = unix_password
|
446
|
+
@modified = true
|
447
|
+
end
|
448
|
+
|
449
|
+
# The String representation of the UNIXGroup object.
|
450
|
+
def to_s
|
451
|
+
"UNIXGroup [(" + RADUM.group_type_to_s(@type) +
|
452
|
+
", RID #{@rid}, GID #{@gid}) #{@distinguished_name}]"
|
453
|
+
end
|
454
|
+
end
|
455
|
+
end
|