4b6be80677f13bb82b997acedcd4771e4a62f549
[public/pyceo-broken.git] / bin / csc-chfn
1 #!/usr/bin/python2.4 --
2 """
3 chfn - change real user name and information
4
5 This utility imitates chfn(1) from the shadow password suite, but makes its
6 changes in the LDAP directory rather than in the passwd file.
7
8 When run from an unprivileged account, authentication will be performed
9 before the account information is changed.
10 """
11 import os, sys, pwd, getopt, PAM
12
13 safe_environment = ['LOGNAME', 'USERNAME', 'USER', 'HOME', 'TERM', 'LANG'
14     'LC_ALL', 'LC_COLLATE', 'LC_CTYPE', 'LC_MESSAGES', 'LC_MONETARY',
15     'LC_NUMERIC', 'LC_TIME', 'UID', 'GID', 'SSH_CONNECTION', 'SSH_AUTH_SOCK',
16     'SSH_CLIENT']
17
18 for key in os.environ.keys():
19     if key not in safe_environment:
20         del os.environ[key]
21
22 os.environ['PATH'] = '/usr/sbin:/usr/bin:/sbin:/bin'
23
24 for pathent in sys.path[:]:
25     if not pathent.find('/usr') == 0:
26         sys.path.remove(pathent)
27
28 from csc.common.excep import InvalidArgument
29 from csc.adm import accounts
30
31 progname = os.path.basename(sys.argv[0])
32
33 OPTION_MAP = {
34             '-f': 'fullname',
35             '-r': 'roomnumber',
36             '-w': 'workphone',
37             '-h': 'homephone',
38             '-o': 'other'
39 }
40 LONG_NAMES = [
41         ('fullname', 'Full Name'),
42         ('roomnumber', 'Room Number'),
43         ('workphone', 'Work Phone'),
44         ('homephone', 'Home Phone'),
45         ('other', 'Other')
46 ]
47 READONLY_FIELDS = [ 'fullname', 'other' ]
48
49 def usage():
50     umesg = "Usage: %s [-f full name] [-r room no] [-w work ph] " + \
51             "[-h home ph] [-o other] [user]"
52     print umesg % progname
53     sys.exit(2)
54
55
56 def whoami():
57     uid = os.getuid()
58     username = os.getlogin()
59     if pwd.getpwnam(username).pw_uid != uid:
60         username = pwd.getpwuid(uid).pw_name
61     return (uid, username)
62
63 def authenticate(username):
64     auth = PAM.pam()
65     auth.start('chsh', username)
66     try:
67         auth.authenticate()
68         auth.acct_mgmt()
69     except PAM.error, resp:
70         print "%s: %s" % (progname, resp.args[0])
71         sys.exit(1)
72
73 def main():
74
75     pwuid, pwnam = whoami()
76
77     gecos_params = {}
78
79     try:
80         options, arguments = getopt.gnu_getopt(sys.argv[1:], 'f:r:w:h:o:')
81         for opt, val in options:
82             gecos_params[OPTION_MAP[opt]] = val
83         if len(arguments) > 1:
84             usage()
85         elif len(arguments) == 1:
86             username = arguments[0]
87         else:
88             username = pwnam
89     except getopt.GetoptError, e:
90         usage()
91
92     for field in READONLY_FIELDS:
93         if field in gecos_params and pwuid:
94             print "%s: Permission denied." % progname
95             sys.exit(1)
96
97     try:
98         if pwuid and pwd.getpwnam(username).pw_uid != pwuid:
99             print "%s: Permission denied." % progname
100             sys.exit(1)
101     except KeyError:
102         print "%s: unknown user %s" % (progname, username)
103         sys.exit(1)
104
105     try:
106         accounts.connect()
107         gecos_raw = accounts.get_gecos(username)
108         gecos = accounts.parse_gecos(gecos_raw)
109
110         if pwuid:
111             authenticate(username)
112
113         if not gecos_params:
114             print "Changing the user information for %s" % username
115             print "Enter the new value, or press ENTER for the default"
116             for field, longname in LONG_NAMES:
117                 if pwuid and field == 'other' and 'other' in READONLY_FIELDS:
118                     continue
119                 if gecos[field] is None:
120                     gecos[field] = ""
121                 if field in READONLY_FIELDS and pwuid:
122                     print "        %s: %s" % (longname, gecos[field])
123                 else:
124                     print "        %s: [%s]:" % (longname, gecos[field]),
125                     new_value = raw_input()
126                     if new_value:
127                         gecos[field] = new_value.strip()
128         else:
129             gecos.update(gecos_params)
130
131         gecos_raw_new = accounts.build_gecos(**gecos)
132         if gecos_raw != gecos_raw_new:
133             accounts.update_gecos(username, gecos_raw_new)
134
135     except InvalidArgument, e:
136         longnames = dict(LONG_NAMES)
137         longname = longnames.get(e.argname, e.argname).lower()
138         print "%s: invalid %s: %s" % (progname, longname, e.argval)
139         sys.exit(1)
140
141 if __name__ == '__main__':
142     exceps = ( accounts.ConfigurationException, accounts.LDAPException,
143             accounts.KrbException, accounts.AccountException )
144     try:
145         main()
146     except KeyboardInterrupt:
147         sys.exit(130) 
148     except IOError, e:
149         print "%s: %s: %s" % (progname, e.filename, e.strerror)
150         sys.exit(1)
151     except exceps, e:
152         print "%s: %s" % (progname, e)
153         sys.exit(1)