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.
@@ -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
@@ -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