2
###########################################################################
3
# Copyright (C) 2004-2006 by Simon Edwards
4
# <simon@simonzone.com>
6
# Copyright: See COPYING file that comes with this distribution
8
###########################################################################
9
# An API for querying and modifying the authorisation database on Unix systems.
11
# The first function that you need to use is getContext(). It returns a
12
# Context object that contains all relevant information concerning
13
# the current authorisation database on this machine.
31
ldaperror = "The LDAP Python Module is not installed, but needed to use LDAP. Install it."
33
def createTempFile(origfile):
34
origstat = os.stat(origfile)
35
tmp_prefix = os.path.basename(origfile) + "."
36
tmp_dir = os.path.dirname(origfile)
38
ret = tempfile.mkstemp(prefix=tmp_prefix, dir=tmp_dir)
40
raise IOError, "Unable to create a new temporary file for " + origfile
42
shutil.copymode(origfile, tmpfile)
43
os.chown(tmpfile, origstat.st_uid, origstat.st_gid)
47
def getContext(editmode=False):
48
"""Get a Context object describing the system's authorisation database.
52
editmode - Set to true if you also wish change the information in this
53
context. Root access is required. Defaults to false.
55
Returns a Context object.
57
If the environmental variable "USERCONFIG_USES_LDAP" is set to "true",
58
userconfig will use LDAP as the backend. This feature is in development
59
and using it is not recommended, it won't work.
62
# Detect what kind of auth system we are running on and create
63
# and initialise the corresponding Context object type.
69
if os.environ["USERCONFIG_USES_LDAP"].lower() == "true":
74
return PwdContext(editmode)
76
print "==================================================================="
78
print "\tYou are using LDAP as backend. This feature is under development"
79
print "\tand it is currently not recommended to use it."
80
print "\tIf you do not want to use LDAP as backend, set the environmental"
81
print "\tvariabale 'USERCONFIG_USES_LDAP' to 'False'."
82
print "==================================================================="
83
return LdapContext(editmode)
85
###########################################################################
88
class Context(object):
89
"""Contains all of the information about the current authorisation
90
database, plus some useful methods for modify this information.
97
self._setDefaultValues()
99
def newUser(self,defaults=False,systemuser=False):
100
"""Create a new UnixUser object.
102
Creates a new blank UnixUser object. The object is not part of the
103
current Context. You need to add it yourself using addUser().
105
Newly allocated UIDs are unique with respect to the list of UnixUser
106
objects in the Context.
109
defaults -- Set to true if the new object should be filled in with
110
reasonable default values for the UID and username.
112
systemuser -- Should the new user be allocated a UID from the system
113
range of UIDs. (default is False)
115
Returns a new UnixUser object.
117
newuserobj = self._createUser()
120
r = xrange(0,self.last_system_uid)
122
r = xrange(self.first_uid,self.last_uid)
124
for u in self._users:
125
if u.getUID()==candiate:
128
newuserobj.setUID(candiate)
131
if self.lookupUsername(u'new_user') is None:
132
newuserobj.setUsername(u'new_user')
136
if self.lookupUsername(u'new_user_'+str(i)) is None:
137
newuserobj.setUsername(u'new_user_'+str(i))
143
"""Get a list of all existing users.
145
Returns an array of UnixUser objects.
147
#print "USERS:", self._users
148
return self._users[:]
151
"""Get a list of all existing groups.
153
Returns an array of UnixGroup objects.
156
self._groups.remove("new_user")
158
print "no user removed"
160
return self._groups[:]
162
def newGroup(self,defaults=False,systemgroup=False):
163
"""Create a new UnixGroup object.
165
Creates a new blank UnixGroup object. The object is not part of the
166
current Context. You need to add it yourself using addGroup().
168
Newly allocated GIDs are unique with respect to the list of UnixGroup
169
objects in the Context.
172
defaults -- Set to true if the new object should be filled in with
173
reasonable default values for the GID and groupname.
175
systemgroup -- Set to True if the newly allocated GID should come
176
from the pool of system group IDs. (default False)
178
Returns a new UnixGroup object.
180
newgroupobj = self._createGroup()
183
r = xrange(0,self.last_system_gid)
185
r = xrange(self.first_gid,self.last_gid)
187
for u in self._groups:
188
if u.getGID()==candiate:
191
newgroupobj.setGID(candiate)
193
if self.lookupGroupname(u'new_group') is None:
194
newgroupobj.setGroupname(u'new_group')
198
if self.lookupGroupname(u'new_user_'+str(i)) is None:
199
newgroupobj.setGroupname(u'new_user_'+str(i))
204
def _createGroup(self):
205
raise NotImplementedError, "Context.newGroup()"
207
def addUser(self,userobj):
208
"""Adds the given user to the authorisation database.
210
This change only takes effect after calling context.save().
213
userobj -- The UnixUser object to add.
215
self._users.append(userobj)
217
def addGroup(self,groupobj):
218
"""Adds the given group to the authorisation database.
220
This change only takes effect after calling context.save().
223
groupobj -- The UnixGroup object to add.
225
if groupobj not in self._groups:
226
self._groups.append(groupobj)
228
def removeUser(self,userobj):
229
"""Removes the given user object from the authorisation database.
231
The user is also removed from all groups.
233
This change only takes effect after calling context.save().
235
for g in userobj.getGroups():
236
userobj.removeFromGroup(g)
238
self._users.remove(userobj)
240
def removeGroup(self,groupobj):
241
"""Removes the given group object from the authorisation database.
243
All users are removed from the group.
245
This change only takes effect after calling context.save().
247
for u in groupobj.getUsers():
248
u.removeFromGroup(groupobj)
250
self._groups.remove(groupobj)
252
def lookupUID(self,uid):
253
"""Lookup a UnixUser object by its numeric user ID.
256
uid -- User ID to lookup, integer.
258
Returns the matching UnixUser object or None if it was not found.
260
for user in self._users:
261
if user.getUID()==uid:
265
def lookupUsername(self,username):
266
"""Lookup a UnixUser object by username.
269
username -- Username to lookup, string.
271
Returns the matching UnixUser object or None if it was not found.
273
for user in self._users:
274
if user.getUsername()==username:
278
def lookupGID(self,gid):
279
"""Lookup a UnixGroup object by its numeric group ID.
282
gid -- Group ID to lookup, integer.
284
Returns the matching UnixGroup object or None if it was not found.
286
for group in self._groups:
287
if group.getGID()==gid:
291
def lookupGroupname(self,groupname):
292
"""Lookup a UnixGroup object by groupname.
294
Returns the matching UnixGroup object or None if it was not found.
296
for group in self._groups:
297
if group.getGroupname()==groupname:
301
def getUserShells(self):
302
"""Get the list of available login shells.
304
Returns an array of strings.
306
if self._shells is None:
308
fhandle = codecs.open('/etc/shells','r',locale.getpreferredencoding())
309
for l in fhandle.readlines():
310
# TODO: strangely this lets some comented lines slip through
311
if len(l.strip()) > 1 and l.strip()[0] is not "#":
312
# Only show existing shells
313
if os.path.isfile(l.strip()):
314
self._shells.append(l.strip())
316
return self._shells[:]
319
"""Synchronises the Context with the underlying operating system.
321
After a successful save, any changes to the Context will be reflected
324
raise NotImplementedError, "Context.save()"
326
def createHomeDirectory(self,userobj):
327
if os.path.exists(userobj.getHomeDirectory()):
328
raise IOError, u"Home directory %s already exists." % userobj.getHomeDirectory()
330
# Copy the skeleton directory over
331
shutil.copytree(self._getSkeletonDirectory(),userobj.getHomeDirectory(),True)
333
# Fix the file ownership stuff
334
uid = userobj.getUID()
335
gid = userobj.getPrimaryGroup().getGID()
336
os.chmod(userobj.getHomeDirectory(),self.dir_mode)
337
#os.system("chmod "+self.dir_mode+" "+userobj.getHomeDirectory())
338
#print "Setting permissions:", userobj.getHomeDirectory(),self.dir_mode
339
os.lchown(userobj.getHomeDirectory(),uid,gid)
340
for root,dirs,files in os.walk(userobj.getHomeDirectory()):
342
os.lchown(os.path.join(root,d),uid,gid)
344
os.lchown(os.path.join(root,f),uid,gid)
346
def removeHomeDirectory(self,userobj):
347
if os.path.exists(userobj.getHomeDirectory()):
348
shutil.rmtree(userobj.getHomeDirectory())
350
def _createUser(self):
351
raise NotImplementedError, "Context._createUser()"
353
def _sanityCheck(self):
355
for u in self._users:
356
if isinstance(u,UnixUser)==False:
357
raise TypeError,"Found an object in the list of users that is not a UnixUser object."
360
raise ValueError, "User ID %i appears more than once." % uid
365
for g in self._groups:
366
if isinstance(g,UnixGroup)==False:
367
raise TypeError,"Found an object in the list of groups that is not a UnixGroup object."
370
raise ValueError, "Group ID %i appears more than once." % gid
374
def _getSkeletonDirectory(self):
377
def _readAdduserConf(self):
378
""" Fill a dictionary with the values from /etc/adduser.conf
379
which then can be used as default values, if the file exists
381
Attention: We're not validating!"""
383
self.adduserconf = '/etc/adduser.conf'
384
if not os.path.isfile(self.adduserconf):
386
fhandle = codecs.open(self.adduserconf,'r',locale.getpreferredencoding())
387
for line in fhandle.readlines():
389
parts = line.split("=")
391
self.defaults[str(parts[0].strip())] = parts[1].strip()
393
def _setDefaultValues(self):
394
""" Set a lot of default values for UIDs and GIDs, try to use the values
395
from /etc/adduser.conf."""
396
self._readAdduserConf()
399
self.skel = self.defaults["SKEL"]
401
self.skel = '/etc/skel'
403
# IDs for new users and groups.
405
self.first_uid = int(self.defaults['FIRST_UID'])
406
except (KeyError,ValueError):
407
self.first_uid = 1000
410
self.last_uid = int(self.defaults["LAST_UID"])
411
except (KeyError,ValueError):
412
self.last_uid = 29999
415
self.first_gid = int(self.defaults["FIRST_GID"])
416
except (KeyError,ValueError):
417
self.first_gid = 1000
420
self.last_gid = int(self.defaults["LAST_GID"])
421
except (KeyError,ValueError):
422
self.last_gid = 65534
424
# Which IDs are system user and system groups?
426
self.first_system_uid = int(self.defaults["FIRST_SYSTEM_UID"])
427
except (KeyError,ValueError):
428
self.first_system_uid = 500
431
self.last_system_uid = int(self.defaults["LAST_SYSTEM_UID"])
432
except (KeyError,ValueError):
433
self.last_system_uid = 65534
436
self.first_system_gid = int(self.defaults["FIRST_SYSTEM_GID"])
437
except (KeyError,ValueError):
438
self.first_system_gid = 500
441
self.last_system_gid = int(self.defaults["LAST_SYSTEM_GID"])
442
except (KeyError,ValueError):
443
self.last_system_gid = 65534
445
# More defaults which might make sense.
447
self.dir_mode = int(self.defaults["DIR_MODE"],8)
448
except (KeyError,ValueError):
449
self.dir_mode = int("0755",8)
450
print "Didn't read default DIR_MODE"
453
self.dhome = self.defaults["DHOME"]
458
self.dshell = self.defaults["DSHELL"]
460
# Will be set in showNewUser()
463
###########################################################################
464
class UnixUser(object):
465
def __init__(self,context):
466
self._context = context
468
self._username = None
471
self._primarygroup = None
473
# List of UnixGroup objects.
477
self._homedirectory = None
478
self._loginshell = None
480
self._islocked = False
484
# FIXME : This should actually be days since epoch or something like this
485
self._passlastchange = 0
486
self._passminimumagebeforechange = 0
487
self._passmaximumage = None
488
self._passexpirewarn = 7
489
self._passexpiredisabledays = None
490
self._disableddays = None
493
primary_group = self._context.lookupGID(self._gid)
494
if primary_group is None:
495
# The GID didn't match an existing group. Quickly make a new group.
496
new_group = self._context.newGroup()
497
new_group.setGID(self._gid)
499
new_group_name = u"group%i" % self._gid
501
while self._context.lookupGroupname(new_group_name) is not None:
503
new_group_name = u"group%i_%i" % (self._gid,i)
504
new_group.setGroupname(new_group_name)
506
self._context.addGroup(new_group)
507
primary_group = new_group
509
self.setPrimaryGroup(primary_group)
510
for group in self._context._groups:
511
if group.contains(self):
512
self._groups.append(group)
515
"""Get the unix user ID.
521
def setUID(self,uid):
522
"""Set the unix user ID.
525
uid -- Integer user id.
529
raise ValueError, "User ID (%i) is a negative number." % uid
532
def isSystemUser(self):
533
"""See if this user is a system user.
535
Returns True or False.
537
return not (self._context.first_uid <= self._uid < self._context.last_uid)
539
def getUsername(self): return self._username
541
def setUsername(self,username): self._username = username
543
def getPrimaryGroup(self):
544
"""Get the primary group for this user.
546
Returns a UnixGroup object.
548
return self._primarygroup
550
def setPrimaryGroup(self,groupobj):
551
"""Set the primary group for this user.
553
If the given group is not part of this user's list of groups, then
557
groupobj -- The group to set as the primary group.
559
self.addToGroup(groupobj)
560
self._primarygroup = groupobj
563
"""Get the list of groups that this user belongs to.
565
The user's primary group is also included in the returned list.
567
Returns a list of UnixGroup objects. Modify the list does not affect
568
this UnixUser object.
570
return self._groups[:]
572
def addToGroup(self,groupobj):
573
"""Add this user to the given group.
576
groupobj -- UnixGroup object.
578
groupobj._addUser(self)
579
if groupobj not in self._groups:
580
self._groups.append(groupobj)
582
def removeFromGroup(self,groupobj):
583
"""Remove this user from the given group.
585
If group is current this user's primary group, then
588
groupobj -- UnixGroup object.
590
groupobj._removeUser(self)
592
self._groups.remove(groupobj)
595
if self._primarygroup is groupobj:
596
if len(self._groups)==0:
597
self._primarygroup = None
599
self._primarygroup = self._groups[0]
601
def setPassword(self,password):
603
space = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQSRTUVWXYZ0123456789./'
606
salt += space[random.randint(0,len(space)-1)]
607
self._encpass = crypt.crypt(password,'$1$'+salt+'$')
609
def isLocked(self): return self._islocked
610
def setLocked(self,locked): self._islocked = locked
612
def getRealName(self):
616
return self._gecos.split(",")[0]
617
except AttributeError:
620
def setRealName(self,realname): self._gecos = realname
621
def getHomeDirectory(self): return self._homedirectory
622
def setHomeDirectory(self,homedirectory): self._homedirectory = homedirectory
623
def getLoginShell(self): return self._loginshell
624
def setLoginShell(self,loginshell): self._loginshell = loginshell
626
# 'None' means that there is no maximum password age.
627
def getMaximumPasswordAge(self): return self._passmaximumage
628
def setMaximumPasswordAge(self,days): self._passmaximumage = days
630
def getMinimumPasswordAgeBeforeChange(self): return self._passminimumagebeforechange
631
def setMinimumPasswordAgeBeforeChange(self,days): self._passminimumagebeforechange = days
632
def getPasswordDisableAfterExpire(self): return self._passexpiredisabledays
633
def setPasswordDisableAfterExpire(self,days): self._passexpiredisabledays = days
634
def getPasswordExpireWarning(self): return self._passexpirewarn
635
def setPasswordExpireWarning(self,days): self._passexpirewarn = days
636
def getLastPasswordChange(self): return self._passlastchange
637
def getExpirationDate(self): return self._disableddays
638
def setExpirationDate(self,unixdate): self._disableddays = unixdate
641
return "%s(%i)" % (self._username,self._uid)
643
def _sanityCheck(self):
644
if self._primarygroup is None:
645
raise ValueError,"Userobj has no primary group!"
646
if self._uid is None:
647
raise ValueError,"Userobj has no UID!"
649
###########################################################################
650
class UnixGroup(object):
651
def __init__(self,context):
652
self._context = context
654
# List of UnixUser objects.
658
self._groupname = None
660
def contains(self,userobj):
661
"""Check if a the given user is a member of this group.
663
Returns True or False.
665
return userobj in self._members
667
def polish(self): pass
668
def isSystemGroup(self):
669
"""Check if this group is a system group.
671
Returns True or False.
673
return not (self._context.first_gid <= self._gid < self._context.last_gid)
674
#return not (500 <= self._gid < 65534)
677
"""Get the unix group ID.
679
Returns the integer group id.
683
def setGID(self,gid):
684
"""Set the unix group ID.
687
gid -- new group id, integer.
691
def getGroupname(self): return self._groupname
692
def setGroupname(self,groupname): self._groupname = groupname
693
def getUsers(self): return self._members[:]
694
def _addUser(self,userobj):
695
if not self.contains(userobj):
696
self._members.append(userobj)
698
def _removeUser(self,userobj):
700
self._members.remove(userobj)
706
return str(self._groupname) + " (" + str(self._gid) + ") " + str([str(u) for u in self._members])
708
def _sanityCheck(self):
711
###########################################################################
712
class PwdContext(Context):
713
#def __init__(self,editmode,passwordfile="etc-passwd",groupfile='etc-group',shadowfile="etc-shadow"):
714
def __init__(self,editmode,passwordfile="/etc/passwd",groupfile='/etc/group',shadowfile="/etc/shadow"):
715
Context.__init__(self)
716
self.__editmode = editmode
717
self.__passwordfile = passwordfile
718
self.__groupfile = groupfile
719
self.__shadowfile = shadowfile
720
self._setDefaultValues()
722
# Read in the password file
723
fhandle = codecs.open(passwordfile,'r',locale.getpreferredencoding())
724
if LockFDRead(fhandle.fileno())==False:
725
raise IOError,"Unable to lock the "+passwordfile+" file."
727
for line in fhandle.readlines():
729
newuserobj = self.newUser(False)
730
newuserobj._initString(line)
731
self._users.append(newuserobj)
733
UnlockFD(fhandle.fileno())
736
# Read the group file
737
fhandle = codecs.open(groupfile,'r',locale.getpreferredencoding())
738
if LockFDRead(fhandle.fileno())==False:
739
raise IOError,"Unable to lock the "+groupfile+" file."
741
for line in fhandle.readlines():
743
newgroupobj = self.newGroup(False)
744
newgroupobj._initString(line)
745
self._groups.append(newgroupobj)
747
UnlockFD(fhandle.fileno())
751
# Load up the info from the shadow file too.
752
fhandle = codecs.open(shadowfile,'r',locale.getpreferredencoding())
753
if LockFDRead(fhandle.fileno())==False:
754
raise IOError,"Unable to lock the "+shadowfile+" file."
756
for line in fhandle.readlines():
759
(username,encpass,passlastchange,passminimumagebeforechange,passmaximumage, \
760
passexpirewarn,passexpiredisabledays,disableddays,reserve) = \
761
tuple(line.strip().split(":"))
762
userobj = self.lookupUsername(username)
763
if userobj is not None:
766
userobj._encpass = encpass
767
if userobj._encpass[0]=='!':
768
userobj._islocked = True
769
userobj._encpass = userobj._encpass[1:]
771
userobj._islocked = False
773
if passlastchange and passlastchange!=u"None":
774
userobj._passlastchange = int(passlastchange)
778
if passminimumagebeforechange=="":
779
passminimumagebeforechange = None
781
passminimumagebeforechange = int(passminimumagebeforechange)
782
if passminimumagebeforechange>=99999:
783
passminimumagebeforechange = None
784
userobj._passminimumagebeforechange = passminimumagebeforechange
786
if passmaximumage=="":
787
passmaximumage = None
789
passmaximumage = int(passmaximumage)
790
if passmaximumage>=99999:
791
passmaximumage = None
792
userobj._passmaximumage = passmaximumage
794
if passexpirewarn=="":
795
passexpirewarn = None
797
passexpirewarn = int(passexpirewarn)
798
if passexpirewarn>=99999:
799
passexpirewarn = None
800
userobj._passexpirewarn = passexpirewarn
802
if passexpiredisabledays=="":
803
userobj._passexpiredisabledays = None
805
userobj._passexpiredisabledays = int(passexpiredisabledays)
807
if disableddays=="" or disableddays==u"99999":
808
userobj._disableddays = None
810
userobj._disableddays = int(disableddays)
812
userobj._reserve = reserve
814
print "Couldn't find",username
818
UnlockFD(fhandle.fileno())
821
for group in self._groups:
823
for user in self._users:
826
def _createUser(self):
829
def _createGroup(self):
830
return PwdGroup(self)
833
if self.__editmode==False:
834
raise IOError, "Can't save, the context was created Read only."
838
# Write out the new password file.
839
(fd, tmpname) = createTempFile(self.__passwordfile)
840
for u in self._users:
841
os.write(fd, u._getPasswdEntry().encode(locale.getpreferredencoding(),'replace'))
842
#print u._getPasswdEntry()
845
# Update the passwd file
846
passwordlock = os.open(self.__passwordfile, os.O_WRONLY) # FIXME encoding
847
if LockFDWrite(passwordlock)==False:
848
raise IOError,"Couldn't get a write lock on "+self.__passwordfile
850
os.rename(tmpname, self.__passwordfile)
852
UnlockFD(passwordlock)
853
os.close(passwordlock)
855
# Write out the new group file
856
(fd, tmpname) = createTempFile(self.__groupfile)
857
origstat = os.stat(self.__groupfile)
858
for g in self._groups:
859
os.write(fd,g._getGroupFileEntry().encode(locale.getpreferredencoding()))
860
#print g._getGroupFileEntry()[:-1]
862
os.chown(tmpname, origstat.st_uid, origstat.st_gid)
864
# Update the group file.
865
grouplock = os.open(self.__groupfile, os.O_WRONLY)
866
if LockFDWrite(grouplock)==False:
867
raise IOError,"Couldn't get write lock on "+self.__groupfile
869
os.rename(tmpname, self.__groupfile)
874
# Write out the new shadow file
875
origstat = os.stat(self.__shadowfile)
876
(fd, tmpname) = createTempFile(self.__shadowfile)
877
for u in self._users:
878
os.write(fd,u._getShadowEntry().encode(locale.getpreferredencoding()))
879
#print u._getShadowEntry()[:-1]
882
# Update the shadow file.
884
# Make sure that it is writable.
885
if (origstat.st_mode & stat.S_IWUSR)==0:
886
os.chmod(self.__shadowfile,origstat.st_mode|stat.S_IWUSR)
888
shadowlock = os.open(self.__shadowfile, os.O_WRONLY)
889
if LockFDWrite(shadowlock)==False:
890
raise IOError,"Couldn't get write lock on "+self.__shadowfile
892
os.rename(tmpname, self.__shadowfile)
897
# set the permissions back to thier default.
898
if (origstat.st_mode & stat.S_IWUSR)==0:
899
os.chmod(self.__shadowfile,origstat.st_mode)
901
###########################################################################
902
class LdapContext(Context):
904
def __init__(self,editmode,server="localhost",admin_dn="",admin_pass=""):
905
""" Connect to the LDAP server and invoke further actions.
907
Context.__init__(self)
908
# admin_dn is DistinguishedName? (or dn, for short)
910
self.baseDN = "dc=vizZzion,dc=net"
912
self.url = "ldap://"+self.server
914
self.ldapserver = ldap.initialize(self.url)
915
self.ldapserver.protocol_version = ldap.VERSION3
917
self.editmode = editmode
918
if not self.editmode:
919
self.ldapserver.simple_bind("admin",admin_pass)
920
print "Connected to ", self.url
922
self._users = self._getUsers()
925
""" Retrieve a list of users from the LDAP server.
928
print "LdapContext._getUsers"
929
searchScope = ldap.SCOPE_SUBTREE
930
retrieveAttributes = None
931
searchFilter = "cn=*"
933
ldap_result_id = self.ldapserver.search(self.baseDN, searchScope, searchFilter, retrieveAttributes)
936
result_type, result_data = self.ldapserver.result(ldap_result_id, 0)
937
if (result_data == []):
940
if result_type == ldap.RES_SEARCH_ENTRY:
941
#print result_data[0][1]
942
#print " --------------------- "
943
result_set.append(result_data[0][1])
945
except ldap.LDAPError, e:
948
if len(result_set) == 0:
953
for entry in result_set:
954
for d in entry.keys():
955
print d, "::", entry[d]
956
print "======== Next User =============="
958
# Walk through result_set and create users.
959
for entry in result_set:
961
name = entry['cn'][0]
962
login = entry['uid'][0]
963
loginshell = entry['loginShell'][0]
964
homedirectory = entry['homeDirectory'][0]
965
uid = entry['uidNumber'][0]
966
gid = entry['gidNumber'][0]
968
#print "\n%d. User: %s\n\tName: %s\n\tShell: %s\n\tHomeDir: %s\n\tUID: %s\n\tGID: %s\n" %\
969
# (count, login, name, loginshell, homedirectory, uid, gid)
970
# Create a new userobject
971
new_user = self._createUser()
972
new_user.setHomeDirectory(homedirectory)
974
new_user.setRealName(name)
975
new_user.setLoginShell(loginshell)
976
new_user.setUsername(login)
977
_users.append(new_user)
978
print "Number of Users:", len(self._users)
981
# Debugging output...
986
def _createUser(self):
987
return LdapUser(self)
989
def _createGroup(self):
990
return LdapGroup(self)
993
print "LdapContext.save() does nothing yet."
995
###########################################################################
996
class LdapUser(UnixUser):
999
return "LdapUser: %s(%i)" % (self._username,self._uid)
1002
###########################################################################
1003
class LdapGroup(UnixGroup):
1006
return "LdapGroup: %s(%i)" % (self._username,self._uid)
1009
###########################################################################
1010
class PwdUser(UnixUser):
1011
def __init__(self,context):
1012
UnixUser.__init__(self,context)
1015
def _initString(self,line):
1016
(self._username,x,self._uid,self._gid,self._gecos,self._homedirectory, \
1017
self._loginshell) = tuple(line.strip().split(":"))
1018
self._uid = int(self._uid)
1019
self._gid = int(self._gid)
1021
def _getPasswdEntry(self):
1022
return u":".join( [self._username,
1025
unicode(self._primarygroup.getGID()),
1027
self._homedirectory,
1028
self._loginshell ] ) + u"\n"
1030
def _getShadowEntry(self):
1032
encpass = u'!' + self._encpass
1034
encpass = self._encpass
1036
if self._passminimumagebeforechange==None:
1037
passminimumagebeforechange = ""
1039
passminimumagebeforechange = str(self._passminimumagebeforechange)
1041
if self._passmaximumage==None:
1042
passmaximumage = u"99999"
1044
passmaximumage = unicode(self._passmaximumage)
1046
if self._disableddays==None:
1049
disableddays = unicode(self._disableddays)
1051
if self._passexpiredisabledays==None:
1052
passexpiredisabledays = u""
1054
passexpiredisabledays = unicode(self._passexpiredisabledays)
1056
if self._passexpirewarn==None:
1057
passexpirewarn = u""
1059
passexpirewarn = unicode(self._passexpirewarn)
1061
return u":".join( [self._username,
1063
unicode(self._passlastchange),
1064
passminimumagebeforechange,
1067
passexpiredisabledays,
1069
self._reserve ])+ u"\n"
1071
###########################################################################
1072
class PwdGroup(UnixGroup):
1073
def __init__(self,context):
1074
UnixGroup.__init__(self,context)
1075
self._memberids = u""
1078
def _initString(self,line):
1079
(self._groupname,self._encpass,self._gid,self._memberids) = tuple(line.strip().split(":"))
1080
self._gid = int(self._gid)
1083
membernames = self._memberids.split(",")
1084
for username in membernames:
1085
userobj = self._context.lookupUsername(username)
1087
self._members.append(userobj)
1089
def _getGroupFileEntry(self):
1090
return u":".join( [ self._groupname,
1093
u",".join([u.getUsername() for u in self._members if u.getPrimaryGroup() is not self])]) + u"\n"
1095
###########################################################################
1100
fcntl.lockf(fd,fcntl.LOCK_SH | fcntl.LOCK_NB)
1107
def LockFDWrite(fd):
1111
fcntl.lockf(fd,fcntl.LOCK_EX | fcntl.LOCK_NB)
1119
fcntl.lockf(fd,fcntl.LOCK_UN)
1121
###########################################################################
1123
if __name__=='__main__':
1125
context = getContext(True)
1127
print "Stopping here..."
1129
#sys.exit(0) ## Remove.
1131
#for user in context.getUsers():
1132
for user in context._users:
1133
print "--------------------------------------------------"
1134
print "UID:",user.getUID()
1135
print "Is system user:",user.isSystemUser()
1136
print "Username:",user.getUsername()
1137
print "Primary Group:",str(user.getPrimaryGroup())
1138
print "Groups:",[str(u) for u in user.getGroups()]
1139
print "Is locked:",user.isLocked()
1140
print "Real name:",user.getRealName()
1141
print "Home Dir:",user.getHomeDirectory()
1142
print "Maximum password age:",user.getMaximumPasswordAge()
1143
print "Minimum password age before change:",user.getMinimumPasswordAgeBeforeChange()
1144
print "Expire warning:",user.getPasswordExpireWarning()
1145
print "Disable after Expire:",user.getPasswordDisableAfterExpire()
1146
#print user._getPasswdEntry()
1149
for group in context.getGroups():
1151
#print group._getGroupFileEntry()