radum 0.0.1
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/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
|