| Class | RADUM::AD |
| In: |
lib/radum/ad.rb
|
| Parent: | Object |
The AD class represents the Active Directory. All opeartions that involve communication between the Container, User, UNIXUser, Group, and UNIXGroup objects and Active Directory are handled by the AD object. The AD object should be the first object created, generally followed by Container objects. The Container object requires an AD object. All other objects require a Container object.
| containers | [R] | The array of Containers in the AD object. |
| domain | [R] | The domain name of the Active Directory. This is calculated from the root attribute. This is automatically made lowercase. |
| gids | [RW] | The array of GID values from UNIXGroup objects in the AD object. This is automatically managed by the other objects and should not be modified directly. |
| ldap | [R] | A handle the the Net::LDAP object used for this AD. |
| min_gid | [RW] | The minimum GID value to use if no other GIDs are found. This defaults to 1000. |
| min_uid | [RW] | The minimum UID value to use if no other UIDs are found. This defaults to 1000. |
| removed_containers | [R] | The array of Containers set for removal in the AD object. |
| rids | [RW] | The array of RID values for User, UNIXUser, Group, and UNIXGroup objects in the AD object. This is automatically managed by the other objects and should not be modified directly. |
| root | [R] | The root of the Active Directory. This is a String representing an LDAP path, such as "dc=example,dc=com". |
| server | [R] | The server hostname or IP address of the Active Directory server. This defaults to "localhost" when an AD is created using AD.new. |
| uids | [RW] | The array of UID values from UNIXUser objects in the AD object. This is automatically managed by the other objects and should not be modified directly. |
| user | [R] | The Active Directory user used to connect to the Active Directory. This is specified using an LDAP path to the user account, without the root component, such as "cn=Administrator,cn=Users". This defaults to "cn=Administrator,cn=Users" when an AD is created using AD.new. |
Create a new AD object that represents an Active Directory environment. This method takes a Hash containing arguments, some of which are required and others optional. The supported arguments follow:
RADUM requires TLS to create user accounts in Active Directory properly, so you will need to make sure you have a certificate server so that you can connect with SSL on port 636. An example instantiation follows:
ad = RADUM::AD.new :root => 'dc=example,dc=com',
:user => 'cn=Administrator,cn=Users',
:password => 'password',
:server => '192.168.1.1'
The :user argument specifies the path to the user account in Active Directory equivalent to the distinguished_name attribute for the user without the :root portion. The :server argument can be an IP address or a hostname. The :root argument is required. If it is not specified, a RuntimeError is raised. Extraneous spaces are removed from the :root and :user arguments. They can have spaces, but extra spaces after any "," characters are removed automatically along with leading and traling white space.
A Container object for "cn=Users" is automatically created and added to the AD object when it is created. This is meant to be a convenience because most, if not all, User and UNIXUser objects will have the "Domain Users" Windows group as their primary Windows group. It is impossible to remove this Container.
# File lib/radum/ad.rb, line 530
530: def initialize(args = {})
531: @root = args[:root] or raise "AD :root argument required."
532: @root.gsub!(/,\s+/, ",")
533: @root.strip!
534: @domain = @root.gsub(/[Dd][Cc]=/, "").gsub(/,/, ".").downcase
535: @user = args[:user] || "cn=Administrator,cn=Users"
536: @user.gsub!(/,\s+/, ",")
537: @user.strip!
538: @password = args[:password]
539: @server = args[:server] || "localhost"
540: @containers = []
541: @removed_containers = []
542: @min_uid = 1000
543: @uids = []
544: @min_gid = 1000
545: @gids = []
546: # RIDs are in a flat namespace, so there's no need to keep track of them
547: # for user or group objects specifically, just in the directory overall.
548: @rids = []
549: @port = 636
550:
551: @ldap = Net::LDAP.new :host => @server,
552: :port => @port,
553: :encryption => :simple_tls,
554: :auth => {
555: :method => :simple,
556: :username => @user + "," + @root,
557: :password => @password
558: }
559:
560: # We add the cn=Users container by default because it is highly likely
561: # that users have the Domain Users Windows group as their Windows
562: # primary group. If we did not do this, there would likely be a ton
563: # of warning messages in the load() method. Keep in mind that containers
564: # automatically add themselves to their AD object.
565: @cn_users = Container.new :name => "cn=Users", :directory => self
566: end
Destroy all references to a Container. This also destroys any references to User, UNIXUser, Group, and UNIXGroup objects the Container owns. This can fail if any of the Group or UNIXGroup objects are the primary Windows group or UNIX main group for any User or UNIXUser objects RADUM knows about.
This method refuses to destroy the "cn=Users" container as a safety measure. There is no error raised in this case, but a warning is logged using RADUM::logger with a log level of LOG_NORMAL.
Any external reference to the Container should be discarded unless it was not possible to fully destroy the Container. This method returns a boolean that indicates if it was possible to fully destroy the Container.
# File lib/radum/ad.rb, line 715
715: def destroy_container(container)
716: # We have to allow removed Container objects to be destroyed because
717: # that's the only way they are deleted from the RADUM environment
718: # when they are deleted from Active Directory.
719: if container == @cn_users
720: RADUM::logger.log("Cannot destroy #{container.name} - safety measure.",
721: LOG_NORMAL)
722: return
723: end
724:
725: can_destroy = true
726:
727: # The next two steps clone the arrays because we are modifying them while
728: # iterting over them at the same time of course.
729: container.users.clone.each do |user|
730: container.destroy_user user
731: end
732:
733: container.groups.clone.each do |group|
734: begin
735: container.destroy_group group
736: rescue RuntimeError => error
737: RADUM::logger.log(error, LOG_NORMAL)
738: can_destroy = false
739: end
740: end
741:
742: # There is no need to worry about sub-container objects.
743:
744: if can_destroy
745: @containers.delete container
746: # Removed Containers can be destroyed as well, so we want to make sure
747: # all references are removed.
748: @removed_containers.delete container
749: container.set_removed
750: end
751:
752: can_destroy
753: end
Find a Container in the AD by name. The search is case-insensitive. The Container is returned if found, otherwise nil is returned.
# File lib/radum/ad.rb, line 591
591: def find_container(name)
592: @containers.find do |container|
593: # This relies on the fact that a container name must be unique in a
594: # directory.
595: container.name.downcase == name.downcase
596: end
597: end
Find a Group or UNIXGroup in the AD object. The first argument is a boolean that indicates if the search should be for removed groups. It is optional and defaults to false. The second required argument is a block. The block is passed each Group and UNIXGroup object and contains the desired testing expression. Attributes for either Group or UNIXGroup objects can be used without worrying about the accessor methods existing. Examples follow:
find_group { |group| group.name == "name" }
find_group { |group| group.gid == 1002 }
find_group(true) { |group| group.name == "name" }
find_group(true) { |group| group.gid == 1002 }
If no block is given the method returns nil. If the Group or UNIXGroup is not found, the method also returns nil, otherwise the Group or UNIXGroup object found is returned.
There are convenient find_group_by_<attribute> methods defined that take care of the example cases here as well, but this method allows any block test to be given.
# File lib/radum/ad.rb, line 1270
1270: def find_group(removed = false)
1271: if block_given?
1272: if removed
1273: search_groups = removed_groups
1274: else
1275: search_groups = groups
1276: end
1277:
1278: found = search_groups.find do |group|
1279: begin
1280: yield group
1281: rescue NoMethodError
1282: end
1283: end
1284:
1285: return found if found
1286: return nil
1287: end
1288:
1289: return nil
1290: end
Find a Group or UNIXGroup in the AD by distinguished name. The search is case-insensitive. The Group or UNIXGroup is returned if found, otherwise nil is returned. Specify the second argument as true if you wish to search for removed Group or UNIXGroup objects.
# File lib/radum/ad.rb, line 1340
1340: def find_group_by_dn(dn, removed = false)
1341: find_group removed do |group|
1342: group.distinguished_name.downcase == dn.downcase
1343: end
1344: end
Find a UNIXGroup in the AD by GID. The UNIXGroup is returned if found, otherwise nil is returned. Specify the second argument as true if you wish to search for removed UNIXGroup objects.
# File lib/radum/ad.rb, line 1327
1327: def find_group_by_gid(gid, removed = false)
1328: find_group(removed) { |group| group.gid == gid }
1329: end
Find a Group or UNIXGroup in the AD by name. The search is case-insensitive. The Group or UNIXGroup is returned if found, otherwise nil is returned. Specify the second argument as true if you wish to search for removed Group or UNIXGroup objects.
# File lib/radum/ad.rb, line 1301
1301: def find_group_by_name(name, removed = false)
1302: find_group removed do |group|
1303: group.name.downcase == name.downcase
1304: end
1305: end
Find a Group or UNIXGroup in the AD by RID. The Group or UNIXGroup is returned if found, otherwise nil is returned. Specify the second argument as true if you wish to search for removed Group or UNIXGroup objects.
# File lib/radum/ad.rb, line 1315
1315: def find_group_by_rid(rid, removed = false)
1316: find_group(removed) { |group| group.rid == rid }
1317: end
Find a User or UNIXUser in the AD object. The first argument is a boolean that indicates if the search should be for removed users. It is optional and defaults to false. The second required argument is a block. The block is passed each User and UNIXUser object and contains the desired testing expression. Attributes for either User or UNIXUser objects can be used without worrying about the accessor methods existing. Examples follow:
find_user { |user| user.username == "username" }
find_user { |user| user.gid == 1002 }
find_user(true) { |user| user.username == "username" }
find_user(true) { |user| user.gid == 1002 }
If no block is given the method returns nil. If the User or UNIXUser is not found, the method also returns nil, otherwise the User or UNIXUser object found is returned.
There are convenient find_user_by_<attribute> methods defined that take care of the example cases here as well, but this method allows any block test to be given.
# File lib/radum/ad.rb, line 812
812: def find_user(removed = false)
813: if block_given?
814: if removed
815: search_users = removed_users
816: else
817: search_users = users
818: end
819:
820: found = search_users.find do |user|
821: begin
822: yield user
823: rescue NoMethodError
824: end
825: end
826:
827: return found if found
828: return nil
829: end
830:
831: return nil
832: end
Find a User or UNIXUser in the AD by distinguished name. The search is case-insensitive. The User or UNIXUser is returned if found, otherwise nil is returned. Specify the second argument as true if you wish to search for removed User or UNIXUser objects.
# File lib/radum/ad.rb, line 882
882: def find_user_by_dn(dn, removed = false)
883: find_user removed do |user|
884: user.distinguished_name.downcase == dn.downcase
885: end
886: end
Find a User or UNIXUser in the AD by RID. The User or UNIXUser is returned if found, otherwise nil is returned. Specify the second argument as true if you wish to search for removed User or UNIXUser objects.
# File lib/radum/ad.rb, line 857
857: def find_user_by_rid(rid, removed = false)
858: find_user(removed) { |user| user.rid == rid }
859: end
Find a UNIXUser in the AD by UID. The UNIXUser is returned if found, otherwise nil is returned. Specify the second argument as true if you wish to search for removed UNIXUser objects.
# File lib/radum/ad.rb, line 869
869: def find_user_by_uid(uid, removed = false)
870: find_user(removed) { |user| user.uid == uid }
871: end
Find a User or UNIXUser in the AD by username. The search is case-insensitive. The User or UNIXUser is returned if found, otherwise nil is returned. Specify the second argument as true if you wish to search for removed User or UNIXUser objects.
# File lib/radum/ad.rb, line 843
843: def find_user_by_username(username, removed = false)
844: find_user removed do |user|
845: user.username.downcase == username.downcase
846: end
847: end
Convert a Group to a UNIXGroup. This method returns the new UNIXGroup if successful, otherwise there will be a RuntimeError somewhere first. The original Group is destroyed with Container#destroy_group and a new UNIXGroup is created with the same information. New UNIX attributes will be added to Active Directory. Any external references to the old Group should be discarded and replaced with the new UNIXGroup object returned. Supported arguments follow:
The :gid argument specifies the UNIX GID value of the UNIXGroup. The :nis_domain defaults to "radum". The use of an NIS domain is not strictly required as one could simply set the right attributes in Active Directory and use LDAP on clients to access that data, but specifying an NIS domain allows for easy editing of UNIX attributes using the GUI tools in Windows, thus the use of a default value.
If the :group argument is not a Group object, a RuntimeError is raised. If the Group has already been removed a RuntimeError is raised. The :gid argument is also checked first in case it already is in use so that the original group is not destroyed by accident due to an error. Note that Container#destroy_group checks to make sure the group is not the primary Windows group for any User or UNIXUser first, so the :group must not be the primary Windows group for any users. If the group is someone‘s primary Windows group a RuntimeError is raised. You will have to modify it by hand in Active Directory if you want to convert it. The primary Windows group check is not done for all users in Active Directory because it is safe to convert unless RADUM thinks the Group is one of its User or UNIXUser object‘s primary Windows group. In that specific case, you‘ll have to modify the group in Active Directory by hand unfortunately.
In this case, unlike when simply creating a UNIXGroup object, all GIDs are checked, including those already in Active Directory.
No changes to the Group happen until AD#sync is called.
# File lib/radum/ad.rb, line 1390
1390: def group_to_unix_group(args = {})
1391: group = args[:group]
1392:
1393: # Make sure we are working with a Group object only.
1394: unless group.instance_of?(Group)
1395: raise ":group argument just be a Group object."
1396: end
1397:
1398: if group.removed?
1399: raise ":group has been removed."
1400: end
1401:
1402: gid = args[:gid]
1403: all_gids = load_ldap_gids
1404:
1405: # The GID must be unique.
1406: if (all_gids + @gids).include?(gid)
1407: raise "GID #{gid} is already in use in the directory."
1408: end
1409:
1410: nis_domain = args[:nis_domain]
1411: # Group attributes.
1412: name = group.name
1413: container = group.container
1414: type = group.type
1415: rid = group.rid
1416: users = group.users.clone
1417: groups = group.groups.clone
1418: removed_users = group.removed_users.clone
1419: removed_groups = group.removed_groups.clone
1420: loaded = group.loaded?
1421:
1422: # Destroy the group now that we have its information. This will fail if
1423: # the group is someone's primary Windows group from the RADUM perspective
1424: # because of the logic I have to employ, but it is fine if the group is
1425: # some other user account's primary Windows group that RADUM doesn't
1426: # know about.
1427: container.destroy_group group
1428:
1429: group = UNIXGroup.new :name => name, :container => container,
1430: :type => type, :rid => rid, :gid => gid,
1431: :nis_domain => nis_domain
1432:
1433: # Set the group to loaded if it was loaded orginally. This sets the
1434: # modified attribute to false, but the actions below will ensure the
1435: # modified attribute is actually true when we are done, which is required
1436: # in order to update the attributes in Active Directory through AD#sync.
1437: group.set_loaded if loaded
1438:
1439: (users + removed_users).each do |user_member|
1440: group.add_user user_member
1441: end
1442:
1443: removed_users.each do |user_member|
1444: group.remove_user user_member
1445: end
1446:
1447: (groups + removed_groups).each do |group_member|
1448: group.add_group group_member
1449: end
1450:
1451: removed_groups.each do |group_member|
1452: group.remove_group group_member
1453: end
1454:
1455: group
1456: end
Load all user and group objects in Active Directory that are in the AD object‘s Containers. This automatically creates User, UNIXUser, Group, and UNIXGroup objects as needed and sets all of their attributes correctly. This can be used to initialize a program using the RADUM module for account management work.
User objects are not created if their primary Windows group is not found during the load. UNIXUser objects are not created if their UNIX main group is not found during the load. Warning messages are printed in each case. Make sure all required Containers are in the AD before loading data from Active Directory to avoid this problem.
You generally should call AD#load to ensure the RADUM system has a valid representation of the Active Directory objects. You can call AD#sync without calling AD#load first, but your object values are authoritative. Unless you set every attribute correctly, unset object attributes will overwrite current values in Active Directory. Note that AD#sync will not touch Active Directory group memberships it does not know about explicitly, so AD#sync will not remove group and user memberships in groups that were not explicitly removed in RADUM. The general RADUM pattern is:
This methods will sliently ignore objects that already exist in the AD object unless the Logger default_level is set to LOG_DEBUG. Therefore, it is possible to load new Container objects after calling this method previously to create new objects. Anything that previously exists will be ignored.
# File lib/radum/ad.rb, line 1641
1641: def load
1642: RADUM::logger.log("[AD #{self.root}] entering load()", LOG_DEBUG)
1643: # This method can be called more than once. After loading users and
1644: # groups, we just want to work with those after they are created. This
1645: # allows the method to be called more than once.
1646: loaded_users = []
1647: loaded_groups = []
1648: # Find all the groups first. We might need one to represent the main
1649: # group of a UNIX user.
1650: group_filter = Net::LDAP::Filter.eq("objectclass", "group")
1651:
1652: @containers.each do |container|
1653: @ldap.search(:base => container.distinguished_name,
1654: :filter => group_filter,
1655: :scope => Net::LDAP::SearchScope_SingleLevel) do |entry|
1656: attr = group_ldap_entry_attr(entry)
1657:
1658: # Skip any Group or UNIXGroup objects that already exist (have this
1659: # :name attribute).
1660: if find_group_by_name(attr[:name])
1661: RADUM::logger.log("\tNot loading group <#{attr[:name]}>: already" +
1662: " exists.", LOG_DEBUG)
1663: next
1664: end
1665:
1666: # Note that groups add themselves to their container.
1667: unless attr[:gid].nil?
1668: attr[:nis_domain] = "radum" unless attr[:nis_domain]
1669: group = UNIXGroup.new :name => attr[:name], :container => container,
1670: :gid => attr[:gid], :type => attr[:type],
1671: :nis_domain => attr[:nis_domain],
1672: :rid => attr[:rid]
1673: group.unix_password = attr[:unix_password] if attr[:unix_password]
1674: loaded_groups.push group
1675: else
1676: group = Group.new :name => attr[:name], :container => container,
1677: :type => attr[:type], :rid => attr[:rid]
1678: loaded_groups.push group
1679: end
1680: end
1681: end
1682:
1683: # Find all the users. The UNIX main group must be set for UNIXUser
1684: # objects, so it will be necessary to search for that.
1685: user_filter = Net::LDAP::Filter.eq("objectclass", "user")
1686:
1687: @containers.each do |container|
1688: @ldap.search(:base => container.distinguished_name,
1689: :filter => user_filter,
1690: :scope => Net::LDAP::SearchScope_SingleLevel) do |entry|
1691: attr = user_ldap_entry_attr(entry)
1692:
1693: # Skip any User or UNIXUser objects that already exist (have this
1694: # :username attribute).
1695: if find_user_by_username(attr[:username])
1696: RADUM::logger.log("\tNot loading user <#{attr[:username]}>:" +
1697: " already exists.", LOG_DEBUG)
1698: next
1699: end
1700:
1701: # Note that users add themselves to their container. We have to have
1702: # found the primary_group already, or we can't make the user. The
1703: # primary group is important information, but it is stored as a RID
1704: # value in the primaryGroupID AD attribute. The group membership
1705: # it defines is defined nowhere else however. We will print a warning
1706: # for any users skipped. This is why the AD object automatically
1707: # adds a cn=Users container.
1708: if attr[:primary_group]
1709: unless attr[:uid].nil? || attr[:gid].nil?
1710: if unix_main_group = find_group_by_gid(attr[:gid])
1711: attr[:nis_domain] = "radum" unless attr[:nis_domain]
1712: user = UNIXUser.new :username => attr[:username],
1713: :container => container,
1714: :primary_group => attr[:primary_group],
1715: :uid => attr[:uid],
1716: :unix_main_group => unix_main_group,
1717: :shell => attr[:shell],
1718: :home_directory => attr[:home_directory],
1719: :nis_domain => attr[:nis_domain],
1720: :disabled => attr[:disabled?],
1721: :rid => attr[:rid]
1722: user.distinguished_name = attr[:distinguished_name]
1723: user.first_name = attr[:first_name] if attr[:first_name]
1724: user.initials = attr[:initials] if attr[:initials]
1725: user.middle_name = attr[:middle_name] if attr[:middle_name]
1726: user.surname = attr[:surname] if attr[:surname]
1727: user.script_path = attr[:script_path] if attr[:script_path]
1728: user.profile_path = attr[:profile_path] if attr[:profile_path]
1729:
1730: if attr[:local_drive] && attr[:local_path]
1731: user.connect_drive_to(attr[:local_drive], attr[:local_path])
1732: elsif attr[:local_path]
1733: user.local_path = attr[:local_path]
1734: end
1735:
1736: user.gecos = attr[:gecos] if attr[:gecos]
1737: user.unix_password = attr[:unix_password] if
1738: attr[:unix_password]
1739: user.shadow_expire = attr[:shadow_expire] if
1740: attr[:shadow_expire]
1741: user.shadow_flag = attr[:shadow_flag] if attr[:shadow_flag]
1742: user.shadow_inactive = attr[:shadow_inactive] if
1743: attr[:shadow_inactive]
1744: user.shadow_last_change = attr[:shadow_last_change] if
1745: attr[:shadow_last_change]
1746: user.shadow_max = attr[:shadow_max] if attr[:shadow_max]
1747: user.shadow_min = attr[:shadow_min] if attr[:shadow_min]
1748: user.shadow_warning = attr[:shadow_warning] if
1749: attr[:shadow_warning]
1750:
1751: if attr[:must_change_password?]
1752: user.force_change_password
1753: end
1754:
1755: loaded_users.push user
1756: else
1757: RADUM::logger.log("Warning: Main UNIX group could not be " +
1758: "found for: " + attr[:username], LOG_NORMAL)
1759: RADUM::logger.log("Not loading #{attr[:username]}.", LOG_NORMAL)
1760: end
1761: else
1762: user = User.new :username => attr[:username],
1763: :container => container,
1764: :primary_group => attr[:primary_group],
1765: :disabled => attr[:disabled?],
1766: :rid => attr[:rid]
1767: user.distinguished_name = attr[:distinguished_name]
1768: user.first_name = attr[:first_name] if attr[:first_name]
1769: user.initials = attr[:initials] if attr[:initials]
1770: user.middle_name = attr[:middle_name] if attr[:middle_name]
1771: user.surname = attr[:surname] if attr[:surname]
1772: user.script_path = attr[:script_path] if attr[:script_path]
1773: user.profile_path = attr[:profile_path] if attr[:profile_path]
1774:
1775: if attr[:local_drive] && attr[:local_path]
1776: user.connect_drive_to(attr[:local_drive], attr[:local_path])
1777: elsif attr[:local_path]
1778: user.local_path = attr[:local_path]
1779: end
1780:
1781: if attr[:must_change_password?]
1782: user.force_change_password
1783: end
1784:
1785: loaded_users.push user
1786: end
1787: else
1788: RADUM::logger.log("Warning: Windows primary group not found for: " +
1789: attr[:username], LOG_NORMAL)
1790: RADUM::logger.log("Not loading #{attr[:username]}.", LOG_NORMAL)
1791: end
1792: end
1793: end
1794:
1795: # Add users to groups, which also adds the groups to the user, etc. The
1796: # Windows primary_group was taken care of when creating the users
1797: # previously. This can happen even if this method is called multiple
1798: # times because it is safe to add users to groups (and vice versa)
1799: # more than once. If the membership already exists, nothing happens.
1800: # Note that in this case, we do have to process all groups again in case
1801: # some of the new users are in already processed groups.
1802: #
1803: # Here it is key to process all groups, even if they were already
1804: # loaded once.
1805: groups.each do |group|
1806: begin
1807: # Note that this takes care of the case where a group was created
1808: # and then AD#load is called before AD#sync. In that case, the group
1809: # is not even in Active Directory yet and the pop method call will
1810: # not be possible because it does not exist. There are other ways
1811: # to check this, but I don't really care if it was not found because
1812: # I assume the user knows what they are doing if they do this type
1813: # of pattern.
1814: entry = @ldap.search(:base => group.distinguished_name,
1815: :filter => group_filter,
1816: :scope => Net::LDAP::SearchScope_BaseObject).pop
1817:
1818: entry.member.each do |member|
1819: # Groups can have groups or users as members, unlike UNIX where
1820: # groups cannot contain group members.
1821: member_group = find_group_by_dn(member)
1822:
1823: if member_group
1824: group.add_group member_group
1825: end
1826:
1827: member_user = find_user_by_dn(member)
1828:
1829: if member_user
1830: group.add_user member_user
1831: end
1832: end
1833: rescue NoMethodError
1834: end
1835: end
1836:
1837: # Set all users and groups as loaded. This has to be done last to make
1838: # sure the modified attribute is correct. The modified attribute needs
1839: # to be false, and it is hidden from direct access by the set_loaded
1840: # method. In this case "all users and groups" means ones we explicitly
1841: # processed because this method can be called more than once. If the
1842: # original object was loaded, but then modified, we don't want to reset
1843: # the modified attribute as well. We also don't want to set the loaded
1844: # attribute and reset the modified attribute for objects that were
1845: # created after an initial call to this method and were skipped on
1846: # later calls.
1847: #
1848: # Here it is key to only touch things explicitly loaded in this call.
1849: loaded_groups.each do |group|
1850: group.set_loaded
1851: end
1852:
1853: loaded_users.each do |user|
1854: user.set_loaded
1855: end
1856:
1857: RADUM::logger.log("[AD #{self.root}] exiting load()", LOG_DEBUG)
1858: end
Load the next free GID value. This is a convenince method that allows one to find the next free GID value. This method returns the next free GID value found while searching from the AD root and any current GID values for UNIXGroup objects that might not be in the Active Directory yet. If nothing is found, the min_gid attribute is returned.
# File lib/radum/ad.rb, line 1891
1891: def load_next_gid
1892: all_gids = load_ldap_gids
1893: next_gid = 0
1894:
1895: # This accounts for any GIDs that might not be in Active Directory yet
1896: # as well.
1897: (all_gids + @gids).uniq.sort.each do |gid|
1898: if next_gid == 0 || next_gid + 1 == gid
1899: next_gid = gid
1900: else
1901: break
1902: end
1903: end
1904:
1905: if next_gid == 0
1906: @min_gid
1907: else
1908: next_gid + 1
1909: end
1910: end
Load the next free UID value. This is a convenience method that allows one to find the next free UID value. This method returns the next free UID value found while searching from the AD root and any current UID values for UNIXUser objects that might not be in the Active Directory yet. If nothing is found, the min_uid attribute is returned.
# File lib/radum/ad.rb, line 1865
1865: def load_next_uid
1866: all_uids = load_ldap_uids
1867: next_uid = 0
1868:
1869: # This accounts for any GIDs that might not be in Active Directory yet
1870: # as well.
1871: (all_uids + @uids).uniq.sort.each do |uid|
1872: if next_uid == 0 || next_uid + 1 == uid
1873: next_uid = uid
1874: else
1875: break
1876: end
1877: end
1878:
1879: if next_uid == 0
1880: @min_uid
1881: else
1882: next_uid + 1
1883: end
1884: end
Set the port number used to communicate with the Active Directory server. This defaults to 636 for TLS in order to create user accounts properly, but can be set here for nonstandard configurations.
# File lib/radum/ad.rb, line 580
580: def port=(port)
581: @port = port
582: @ldap.port = port
583: end
Remove a Container from the AD. This attempts to set the Container object‘s removed attribute to true as well as remove any User, UNIXUser, Group, and UNIXGroup objects it contains. If any Group or UNIXGroup object cannot be removed because it is a dependency, the Container cannot be fully removed either, but all objects that can be removed will be removed. This can happen if a Group or UNIXGroup is another User or UNIXUser object‘s primary Windows group or UNIX main group and that User or UNIXUser is not in the same Container. Removed Container objects are ignored.
Note that this method might succeed based on the user and group objects it knows about, but it still might fail when AD#sync is called because a more extensive Active Directory search will be performed at that point. In any case, all users will be removed and all groups (and the Container) if possible. This method is greedy in that it tries to remove as many objects from Active Directory as possible.
This method refuses to remove the "cn=Users" container as a safety measure. There is no error raised in this case, but a warning is logged using RADUM::logger with a log level of LOG_NORMAL.
Any external reference to the Container should be discarded unless it was not possible to fully remove the Container. This method returns a boolean that indicates if it was possible to fully remove the Container.
# File lib/radum/ad.rb, line 646
646: def remove_container(container)
647: return if container.removed?
648:
649: if container == @cn_users
650: RADUM::logger.log("Cannot remove #{container.name} - safety measure.",
651: LOG_NORMAL)
652: return false
653: end
654:
655: can_remove = true
656:
657: # The next two steps clone the arrays because we are modifying them while
658: # iterting over them at the same time of course.
659: container.users.clone.each do |user|
660: container.remove_user user
661: end
662:
663: container.groups.clone.each do |group|
664: begin
665: container.remove_group group
666: rescue RuntimeError => error
667: RADUM::logger.log(error, LOG_NORMAL)
668: can_remove = false
669: end
670: end
671:
672: @containers.each do |current_container|
673: next if current_container == container
674:
675: if current_container.name =~ /#{container.name}/
676: RADUM::logger.log("Container #{container.name} contains container " +
677: "#{current_container.name}.", LOG_NORMAL)
678: can_remove = false
679: end
680: end
681:
682: if can_remove
683: @containers.delete container
684:
685: unless @removed_containers.include?(container)
686: @removed_containers.push container
687: end
688:
689: container.set_removed
690: else
691: RADUM::logger.log("Cannot fully remove container #{container.name}.",
692: LOG_NORMAL)
693: end
694:
695: can_remove
696: end
Returns an Array of all removed Group and UNIXGroup objects in the AD.
# File lib/radum/ad.rb, line 1225
1225: def removed_groups
1226: all_removed_groups = []
1227:
1228: @containers.each do |container|
1229: all_removed_groups += container.removed_groups
1230: end
1231:
1232: # We also need to check removed Containers too because they can have
1233: # removed groups too.
1234: @removed_containers.each do |container|
1235: all_removed_groups += container.removed_groups
1236: end
1237:
1238: all_removed_groups
1239: end
Returns an Array of all removed User and UNIXUser objects in the AD.
# File lib/radum/ad.rb, line 767
767: def removed_users
768: all_removed_users = []
769:
770: @containers.each do |container|
771: all_removed_users += container.removed_users
772: end
773:
774: # We also need to check removed Containers too because they can have
775: # removed users too.
776: @removed_containers.each do |container|
777: all_removed_users += container.removed_users
778: end
779:
780: all_removed_users
781: end
Synchronize all modified Container, User, UNIXUser, Group, and UNIXGroup objects to Active Directory. This will create entries as needed after checking to make sure they do not already exist. New attributes will be added, unset attributes will be removed, and modified attributes will be updated automatically. Removed objects will be deleted from Active Directory.
You generally should call AD#load to ensure the RADUM system has a valid representation of the Active Directory objects. You can call AD#sync without calling AD#load first, but your object values are authoritative. Unless you set every attribute correctly, unset object attributes will overwrite current values in Active Directory. Note that AD#sync will not touch Active Directory group memberships it does not know about explicitly, so AD#sync will not remove group and user memberships in groups that were not explicitly removed in RADUM. The general RADUM pattern is:
# File lib/radum/ad.rb, line 1934
1934: def sync
1935: RADUM::logger.log("[AD #{self.root}] entering sync()", LOG_DEBUG)
1936: users_to_destroy = []
1937: containers_to_destroy = []
1938:
1939: # First, delete any users that have been removed from a container here.
1940: # We need to remove users first because a group cannot be removed if
1941: # a user has it as their primary Windows group. Just in case, we remove
1942: # the removed users first. The same applies if the group is some other
1943: # user's UNIX main group. The code in this module makes sure that doesn't
1944: # happen for objects it knows about, but there could be others in Active
1945: # Directory the module does not know about.
1946: removed_users.each do |user|
1947: delete_user user
1948: # The delete_user() method cannot destroy a user because the system
1949: # still needs to reference the user when update_group() is called so
1950: # that the deleted user will end up having their UNIX attributes removed
1951: # from a UNIXGroup if that applies when updating the specific group.
1952: # This is not needed for Group or UNIXGroup objects because they only
1953: # have membership from the Windows perspective - and that's updated by
1954: # simply deleting the Group or UNIXGroup.
1955: users_to_destroy.push user
1956: end
1957:
1958: # Second, remove any groups that have been removed from a contianer here.
1959: removed_groups.each do |group|
1960: # This method checks if the group is some other user's primary Windows
1961: # group by searching the entire Active Directory. A group cannot be
1962: # removed if it is any user's primary Windows group. The same applies
1963: # if the group is some other user's UNIX main group. The code in this
1964: # module makes sure that doesn't happen for objects it knows about, but
1965: # there could be others in Active Directory the module does not know
1966: # about, hence the checks in delete_group().
1967: delete_group group
1968: end
1969:
1970: # Third, remove any containers that have been removed. This can only be
1971: # done after all the user and group removals hae been dealt with. This
1972: # can still fail if there are any objects in Active Directory inside of
1973: # the container (such as another container). Note that the
1974: # AD#remove_container method makes sure that a container is not removed
1975: # if it contains another container in the first place.
1976: @removed_containers.each do |container|
1977: delete_container container
1978: # The delete_container() method cannot destroy a container because the
1979: # system still needs a reference when update_group() is called for
1980: # users that have been deleted from the system to ensure their
1981: # membership in UNIX groups from the UNIX perspective is deleted.
1982: # The removed_users() method depends on the container still being
1983: # there for this later processing.
1984: containers_to_destroy.push container
1985: end
1986:
1987: # Fourth, create any containers or organizational units that do not
1988: # already exist.
1989: @containers.each do |container|
1990: # This method only creates containers that do not already exist. Since
1991: # containers are not loaded directly at first, their status is directly
1992: # tested in the method.
1993: create_container container
1994: end
1995:
1996: # Fifth, make sure any groups that need to be created are added to Active
1997: # Directory.
1998: groups.each do |group|
1999: # This method checks if the group actually needs to be created or not.
2000: create_group group
2001: end
2002:
2003: # Sixth, make sure any users that need to be created are added to Active
2004: # Directory.
2005: users.each do |user|
2006: # This method checks if the user actually needs to be created or not.
2007: create_user user
2008: end
2009:
2010: # Seventh, update any modified attributes on each group.
2011: groups.each do |group|
2012: # This method figures out what attributes need to be updated when
2013: # compared to Active Directory. All objects should exist in Active
2014: # Directory at this point, but the method handles cases where the
2015: # object is not in Active Directory by skipping the update in that
2016: # case.
2017: update_group group
2018: end
2019:
2020: # Eighth, update any modified attributs on each user.
2021: users.each do |user|
2022: # This method figures out what attributes need to be updated when
2023: # compared to Active Directory. All objects should exist in Active
2024: # Directory at this point, but the method handles cases where the
2025: # object is not in Active Directory by skipping the update in that
2026: # case.
2027: update_user user
2028: end
2029:
2030: # Finally, destroy any user and container objects that were deleted.
2031: users_to_destroy.each do |user|
2032: RADUM::logger.log("[AD #{self.root}] destroying user" +
2033: " <#{user.username}> at end of sync().", LOG_DEBUG)
2034: user.container.destroy_user user
2035: end
2036:
2037: # Container objects are destroyed even if they were not removed from
2038: # Active Directory through LDAP. If they were destroyed or removed RADUM,
2039: # it is safe to discard their reference here. If they were not deleted
2040: # from Active Directory, that means there was some reference we were not
2041: # aware of in Active Directory - but we can still forget about them.
2042: containers_to_destroy.each do |container|
2043: RADUM::logger.log("[AD #{self.root}] destroying container" +
2044: " <#{container.name}> at end of sync().", LOG_DEBUG)
2045: destroy_container container
2046: end
2047:
2048: RADUM::logger.log("[AD #{self.root}] exiting sync()", LOG_DEBUG)
2049: end
Convert a UNIXGroup to a Group. This method returns the new Group if successful, otherwise there will be a RuntimeError somewhere first. The original UNIXGroup is destroyed with Container#destroy_group and a new Group is created with the same information where applicable. Old UNIX attributes will be removed from Active Directory if possible immediately (not waiting on AD#sync because AD#sync will think this is now only a Group object with no UNIX attributes). Any external references to the old UNIXGroup should be discarded and replaced with the new Group object returned. Supported arguments follow:
The :group argument is the UNIXGroup to convert. If the :group argument is not a UNIXGroup object, a RuntimeError is raised. If the UNIXGroup has already been removed a RuntimeError is raised. The :remove_unix_users argument is a boolean flag that determines if UNIXUser objects who were members of the UNIXGroup from the Windows perspective should be removed as members when converting to a Group object. UNIXUser objects are members from the Windows perspective as well by default because they are members from the UNIX perspective. This is the default behavior in RADUM. The default action is to not remove their Windows user memberships when converting a UNIXGroup to a Group.
Note that Container#destroy_group checks to make sure the group is not the primary Windows group or UNIX main group for any User or UNIXUser, so the :group must not be the primary Windows group or UNIX main group for any users. If the group is someone‘s primary Windows group or UNIX main group a RuntimeError will be raised. You will have to modify it by hand in Active Directory if you want to convert it. The UNIX main group condition is checked for all users in Active Directory before attempting to use Container#destroy_group to ensure the conversion is safe. The primary Windows group check is not done for all users in Active Directory because it is safe to convert unless RADUM thinks the UNIXGroup is one of its User or UNIXUser object‘s primary Windows group. In that specific case, you‘ll have to modify the group in Active Directory by hand unfortunately.
UNIX attributes are removed from Active Directory immedately if it is actually possible to destroy the UNIXGroup properly without waiting for AD#sync to be called.
# File lib/radum/ad.rb, line 1504
1504: def unix_group_to_group(args = {})
1505: RADUM::logger.log("[AD #{self.root}] entering unix_group_to_group()",
1506: LOG_DEBUG)
1507: group = args[:group]
1508:
1509: # Make sure we are working with a UNIXGroup object only.
1510: unless group.instance_of?(UNIXGroup)
1511: raise ":group argument just be a UNIXGroup object."
1512: end
1513:
1514: if group.removed?
1515: raise ":group has been removed."
1516: end
1517:
1518: remove_unix_users = args[:remove_unix_users] || false
1519:
1520: # Group attributes.
1521: name = group.name
1522: container = group.container
1523: type = group.type
1524: rid = group.rid
1525: users = group.users.clone
1526: groups = group.groups.clone
1527: removed_users = group.removed_users.clone
1528: removed_groups = group.removed_groups.clone
1529: loaded = group.loaded?
1530:
1531: # Make sure the group is not someone's UNIX main group for all users
1532: # in Active Directory before trying to destroy the group. The
1533: # Container#destroy_group method only checks for objects in RADUM
1534: # itself.
1535: if ldap_is_unix_main_group?(group)
1536: raise ":group is someone's UNIX main group."
1537: end
1538:
1539: # Destroy the group now that we have its information. This will fail if
1540: # the group is someone's primary Windows group from the RADUM perspective
1541: # because of the logic I have to employ, but it is fine if the group is
1542: # some other user account's primary Windows group that RADUM doesn't
1543: # know about.
1544: container.destroy_group group
1545:
1546: # If the group was destroyed and we got this far, we need to remove
1547: # any of its UNIX attributes in Active Directory directly. We do need
1548: # to make sure it is actually there first of course.
1549: group_filter = Net::LDAP::Filter.eq("objectclass", "group")
1550: found = @ldap.search(:base => group.distinguished_name,
1551: :filter => group_filter,
1552: :scope => Net::LDAP::SearchScope_BaseObject,
1553: :return_result => false)
1554:
1555: unless found == false
1556: ops = [
1557: [:replace, :gidNumber, nil],
1558: [:replace, :msSFU30NisDomain, nil],
1559: [:replace, :unixUserPassword, nil],
1560: [:replace, :memberUid, nil],
1561: [:replace, :msSFU30PosixMember, nil]
1562: ]
1563:
1564: RADUM::logger.log("\tRemoving groups's previous UNIX attributes.",
1565: LOG_DEBUG)
1566: RADUM::logger.log("\n" + ops.to_yaml + "\n\n", LOG_DEBUG)
1567: @ldap.modify :dn => group.distinguished_name, :operations => ops
1568: check_ldap_result
1569: end
1570:
1571: group = Group.new :name => name, :container => container,
1572: :type => type, :rid => rid
1573:
1574: # Set the group to loaded if it was loaded orginally. This sets the
1575: # modified attribute to false, but the actions below will ensure the
1576: # modified attribute is actually true when we are done, which is required
1577: # in order to update the attributes in Active Directory through AD#sync.
1578: group.set_loaded if loaded
1579:
1580: (users + removed_users).each do |user_member|
1581: group.add_user user_member
1582: end
1583:
1584: removed_users.each do |user_member|
1585: group.remove_user user_member
1586: end
1587:
1588: (groups + removed_groups).each do |group_member|
1589: group.add_group group_member
1590: end
1591:
1592: removed_groups.each do |group_member|
1593: group.remove_group group_member
1594: end
1595:
1596: # An extra step to remove any UNIXUser objects if that was requested.
1597: if remove_unix_users
1598: group.users.clone.each do |user|
1599: group.remove_user user if user.instance_of?(UNIXUser)
1600: end
1601: end
1602:
1603: RADUM::logger.log("[AD #{self.root}] exiting unix_group_to_group()",
1604: LOG_DEBUG)
1605: group
1606: end
Convert a UNIXUser to a User. This method returns the new User if successful, otherwise there will be a RuntimeError somewhere first. The original UNIXUser is destroyed with Container#destroy_user and a new User is created with the same information where applicable. Old UNIX attributes will be removed from Active Directory if possible immediately (not waiting on AD#sync because AD#sync will think this is now only a User object with no UNIX attributes). Any external references to the old UNIXUser should be discarded and replaced with the new User object returned. Supported arguments follow:
The :user argument is the UNIXUser to convert. If the :user argument is not a UNIXUser object, a RuntimeError is raised. If the UNIXUser has already been removed a RuntimeError is raised. The :remove_unix_groups argument is a boolean flag that determines if the new User object should continue to be a member of the UNIXGroup objects it was previously. UNIXUser objects are members of their UNIXGroup objects from the Windows perspective by default because they are members from the UNIX perspective. This is the default behavior in RADUM. The default action is to not remove their Windows group memberships when converting a UNIXUser to a User.
UNIX attributes are removed from Active Directory immedately if it is actually possible to destroy the UNIXUser properly without waiting for AD#sync to be called.
# File lib/radum/ad.rb, line 1056
1056: def unix_user_to_user(args = {})
1057: RADUM::logger.log("[AD #{self.root}] entering unix_user_to_user()",
1058: LOG_DEBUG)
1059: user = args[:user]
1060:
1061: # Make sure we are working with a UNIXUser object only.
1062: unless user.instance_of?(UNIXUser)
1063: raise ":user argument must be a UNIXUser object."
1064: end
1065:
1066: if user.removed?
1067: raise ":user has been removed."
1068: end
1069:
1070: remove_unix_groups = args[:remove_unix_groups] || false
1071:
1072: # User attributes.
1073: username = user.username
1074: container = user.container
1075: primary_group = user.primary_group
1076: # This is needed later if removal from UNIXGroup objects was requested.
1077: unix_main_group = user.unix_main_group
1078: disabled = user.disabled?
1079: rid = user.rid
1080: first_name = user.first_name
1081: initials = user.initials
1082: middle_name = user.middle_name
1083: surname = user.surname
1084: script_path = user.script_path
1085: profile_path = user.profile_path
1086: local_path = user.local_path
1087: local_drive = user.local_drive
1088: password = user.password
1089: must_change_password = user.must_change_password?
1090: groups = user.groups.clone
1091: removed_groups = user.removed_groups.clone
1092: loaded = user.loaded?
1093:
1094: # Destroy the user now that we have its information.
1095: container.destroy_user user
1096:
1097: # If the user was destroyed and we got this far, we need to remove
1098: # any of its UNIX attributes in Active Directory directly. We do need
1099: # to make sure it is actually there first of course.
1100: user_filter = Net::LDAP::Filter.eq("objectclass", "user")
1101: found = @ldap.search(:base => user.distinguished_name,
1102: :filter => user_filter,
1103: :scope => Net::LDAP::SearchScope_BaseObject,
1104: :return_result => false)
1105:
1106: unless found == false
1107: ops = [
1108: [:replace, :loginShell, nil],
1109: [:replace, :unixHomeDirectory, nil],
1110: [:replace, :msSFU30NisDomain, nil],
1111: [:replace, :gecos, nil],
1112: [:replace, :unixUserPassword, nil],
1113: [:replace, :shadowExpire, nil],
1114: [:replace, :shadowFlag, nil],
1115: [:replace, :shadowInactive, nil],
1116: [:replace, :shadowLastChange, nil],
1117: [:replace, :shadowMax, nil],
1118: [:replace, :shadowMin, nil],
1119: [:replace, :shadowWarning, nil],
1120: [:replace, :gidNumber, nil],
1121: [:replace, :uidNumber, nil]
1122: ]
1123:
1124: RADUM::logger.log("\tRemoving user's previous UNIX attributes.",
1125: LOG_DEBUG)
1126: RADUM::logger.log("\n" + ops.to_yaml + "\n\n", LOG_DEBUG)
1127: @ldap.modify :dn => user.distinguished_name, :operations => ops
1128: check_ldap_result
1129: end
1130:
1131: user = User.new :username => username, :container => container,
1132: :primary_group => primary_group, :disabled => disabled,
1133: :rid => rid
1134:
1135: # Set the user to loaded if it was loaded orginally. This sets the
1136: # modified attribute to false, but the actions below will ensure the
1137: # modified attribute is actually true when we are done, which is required
1138: # in order to update the attributes in Active Directory through AD#sync.
1139: user.set_loaded if loaded
1140:
1141: # Set other User attributes.
1142: user.first_name = first_name
1143: user.initials = initials
1144: user.middle_name = middle_name
1145: user.surname = surname
1146: user.script_path = script_path
1147: user.profile_path = profile_path
1148:
1149: # Figure out the Windows home directory type attributes.
1150: if local_path && local_drive
1151: user.connect_drive_to local_drive, local_path
1152: elsif local_path
1153: user.local_path = local_path
1154: end
1155:
1156: user.password = password
1157:
1158: if must_change_password
1159: user.force_change_password
1160: end
1161:
1162: (groups + removed_groups).each do |group_member|
1163: user.add_group group_member
1164: end
1165:
1166: removed_groups.each do |group_member|
1167: user.remove_group group_member
1168: end
1169:
1170: # An extra step to remove any UNIXGroup objects if that was requested.
1171: if remove_unix_groups
1172: user.groups.clone.each do |group|
1173: if group.instance_of?(UNIXGroup)
1174: # The user is not a member of their primary Windows group in the
1175: # groups array for the user, so no check is needed. In that case,
1176: # nothing happens.
1177: user.remove_group group
1178:
1179: # Don't try to remove a membership in UNIX LDAP attributes that does
1180: # not exist.
1181: if group != unix_main_group
1182: # Immediately remove UNIX group membership through UNIX LDAP
1183: # attributes if the UNIXGroup is actually in Active Directory at
1184: # this point.
1185: group_filter = Net::LDAP::Filter.eq("objectclass", "group")
1186: found = @ldap.search(:base => group.distinguished_name,
1187: :filter => group_filter,
1188: :scope => Net::LDAP::SearchScope_BaseObject,
1189: :return_result => false)
1190:
1191: unless found == false
1192: ops = [
1193: [:delete, :memberUid, user.username],
1194: [:delete, :msSFU30PosixMember, user.distinguished_name]
1195: ]
1196:
1197: RADUM::logger.log("\tRemoving UNIX LDAP attribute membership" +
1198: " for <#{group.name}>.", LOG_DEBUG)
1199: RADUM::logger.log("\n" + ops.to_yaml + "\n\n", LOG_DEBUG)
1200: @ldap.modify :dn => group.distinguished_name, :operations => ops
1201: check_ldap_result
1202: end
1203: end
1204: end
1205: end
1206: end
1207:
1208: RADUM::logger.log("[AD #{self.root}] exiting unix_user_to_user()",
1209: LOG_DEBUG)
1210: user
1211: end
Convert a User to a UNIXUser. This method returns the new UNIXUser if successful, otherwise there will be a RuntimeError somewhere first. The original User is destroyed with Container#destroy_user and a new UNIXUser is created with the same information. New UNIX attributes will be added to Active Directory. Any external references to the old User should be discarded and replaced with the new UNIXUser object returned. Supported arguments follow:
The :uid argument specifies the UNIX UID value of the UNIXUser. The :unix_main_group argument must be a UNIXGroup object or a RuntimeError is raised. The :nis_domain defaults to "radum". The use of an NIS domain is not strictly required as one could simply set the right attributes in Active Directory and use LDAP on clients to access that data, but specifying an NIS domain allows for easy editing of UNIX attributes using the GUI tools in Windows, thus the use of a default value.
If the :user argument is not a User object, a RuntimeError is raised. If the User has already been removed a RuntimeError is raised. The :uid argument is also checked first in case it already is in use so that the original user is not destroyed by accident due to an error.
In this case, unlike when simply creating a UNIXUser object, all UIDs are checked, including those already in Active Directory.
No changes to the User happen until AD#sync is called.
# File lib/radum/ad.rb, line 929
929: def user_to_unix_user(args = {})
930: user = args[:user]
931:
932: # Make sure we are working with a User object only.
933: unless user.instance_of?(User)
934: raise ":user argument must be a User object."
935: end
936:
937: if user.removed?
938: raise ":user has been removed."
939: end
940:
941: uid = args[:uid]
942: all_uids = load_ldap_uids
943:
944: # The UID must be unique.
945: if (all_uids + @uids).include?(uid)
946: raise "UID #{uid} is already in use in the directory."
947: end
948:
949: unix_main_group = args[:unix_main_group]
950:
951: unless unix_main_group.instance_of?(UNIXGroup)
952: raise ":unix_main_group is not a UNIXGroup object."
953: end
954:
955: shell = args[:shell]
956: home_directory = args[:home_directory]
957: nis_domain = args[:nis_domain]
958: # User attributes.
959: username = user.username
960: container = user.container
961: primary_group = user.primary_group
962: disabled = user.disabled?
963: rid = user.rid
964: first_name = user.first_name
965: initials = user.initials
966: middle_name = user.middle_name
967: surname = user.surname
968: script_path = user.script_path
969: profile_path = user.profile_path
970: local_path = user.local_path
971: local_drive = user.local_drive
972: password = user.password
973: must_change_password = user.must_change_password?
974: groups = user.groups.clone
975: removed_groups = user.removed_groups.clone
976: loaded = user.loaded?
977:
978: # Destroy the user now that we have its information.
979: container.destroy_user user
980: user = UNIXUser.new :username => username, :container => container,
981: :primary_group => primary_group,
982: :disabled => disabled, :rid => rid, :uid => uid,
983: :unix_main_group => unix_main_group,
984: :shell => shell, :home_directory => home_directory,
985: :nis_domain => nis_domain
986:
987: # Set the user to loaded if it was loaded orginally. This sets the
988: # modified attribute to false, but the actions below will ensure the
989: # modified attribute is actually true when we are done, which is required
990: # in order to update the attributes in Active Directory through AD#sync.
991: user.set_loaded if loaded
992:
993: # Set other User attributes.
994: user.first_name = first_name
995: user.initials = initials
996: user.middle_name = middle_name
997: user.surname = surname
998: user.script_path = script_path
999: user.profile_path = profile_path
1000:
1001: # Figure out the Windows home directory type attributes.
1002: if local_path && local_drive
1003: user.connect_drive_to local_drive, local_path
1004: elsif local_path
1005: user.local_path = local_path
1006: end
1007:
1008: user.password = password
1009:
1010: if must_change_password
1011: user.force_change_password
1012: end
1013:
1014: (groups + removed_groups).each do |group_member|
1015: user.add_group group_member
1016: end
1017:
1018: removed_groups.each do |group_member|
1019: user.remove_group group_member
1020: end
1021:
1022: user
1023: end