ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/ccs/admin/admin.py
Revision: 681
Committed: 2010-07-13T02:19:33-07:00 (14 years, 11 months ago) by douglas
Content type: text/x-python
File size: 6339 byte(s)
Log Message:
Add mksh to available shells; options with argparse (not sure if I like it).

File Contents

# User Rev Content
1 douglas 600 #!/usr/bin/env python
2 douglas 585 # CCS Computer Science
3 douglas 590 # Admin
4 douglas 585 #
5     # Douglas Thrift
6     #
7     # $Id$
8    
9 douglas 681 import argparse
10 douglas 591 import common
11 douglas 585 import ldap
12 douglas 598 import os
13     import psycopg2
14 douglas 681 import re
15 douglas 598 import shutil
16 douglas 681 import subprocess
17 douglas 592 import sys
18 douglas 680 import warnings
19 douglas 585
20 douglas 680 with warnings.catch_warnings():
21     warnings.filterwarnings('ignore', 'the sets module is deprecated', DeprecationWarning)
22 douglas 598
23     import MySQLdb
24    
25 douglas 585 MASTER = 'zweihander.ccs.ucsb.edu'
26 douglas 600 SLAVE = 'wireless.ccs.ucsb.edu'
27     MASTER_URI = 'ldaps://' + MASTER
28     SLAVE_URI = 'ldaps://' + SLAVE
29 douglas 585 BASE = 'dc=ccs,dc=ucsb,dc=edu'
30 douglas 598 PEOPLE = 'ou=People,' + BASE
31     GROUP = 'ou=Group,' + BASE
32 douglas 606 SECRET = '/ccs/etc/secret'
33 douglas 592 SHELLS = map(lambda system: 'ucsbCcs' + system.capitalize(), common.SYSTEMS)
34 douglas 598 SAMBA_SID = 'S-1-5-21-3739982181-3886045993-82308153-%u'
35 douglas 681 USER = re.compile('^[a-z0-9]{1,16}$')
36     NAME = re.compile('^[^:]+$')
37     INVALID_USER = 'Invalid user name: "%s"'
38     INVALID_NAME = 'Invalid full name: "%s"'
39     _PARSER = argparse.ArgumentParser(add_help = False)
40 douglas 585
41 douglas 681 class _VersionAction(argparse.Action):
42     def __call__(self, parser, namespace, values, option_string = None):
43     message = '%s %s (CCS CS)\n' % (parser.prog, subprocess.Popen('svnversion', stdout = subprocess.PIPE, cwd = os.path.dirname(__file__)).communicate()[0].strip())
44    
45     if values not in ('s', 'short'):
46     name = os.uname()
47     message = '%s - %s %s %s\n' % (message.strip(), name[0], name[2], name[4])
48    
49     parser.exit(message = message)
50    
51     _PARSER.add_argument('-V', '--version', action = _VersionAction, nargs = '?', choices = ('s', 'short'), help = 'show version information and exit', dest = argparse.SUPPRESS)
52 douglas 585 ldap.set_option(ldap.OPT_PROTOCOL_VERSION, 3)
53     ldap.set_option(ldap.OPT_X_TLS_CACERTFILE, '/ccs/ssl/ccscert.pem')
54     ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_DEMAND)
55    
56     def _user(user):
57 douglas 598 return 'uid=%s,%s' % (user, PEOPLE)
58 douglas 585
59 douglas 598 def _group(group):
60     return 'cn=%s,%s' % (group, GROUP)
61    
62 douglas 681 def _program():
63     return os.path.splitext(os.path.basename(sys.argv[0]))[0]
64    
65     def ldap_connection(root = True):
66 douglas 600 connection = ldap.initialize(MASTER_URI)
67 douglas 585
68 douglas 681 if root:
69     with open(SECRET, 'rb') as secret:
70     connection.simple_bind_s(_user('root'), secret.read())
71     else:
72     connection.simple_bind_s()
73 douglas 585
74     return connection
75    
76 douglas 591 def master():
77 douglas 592 return common.HOST == MASTER
78 douglas 591
79 douglas 593 def run(errors):
80     if errors:
81     for host, error in errors.iteritems():
82     sys.stderr.write('%s: %s\n' % (host, error))
83 douglas 592
84     sys.exit(1)
85    
86 douglas 593 def error(error):
87 douglas 681 sys.exit('%s: %s' % (_program(), error))
88 douglas 593
89 douglas 592 def eof():
90     print
91    
92     sys.exit(130)
93    
94 douglas 681 def parser(**kwargs):
95     kwargs['prog'] = _program()
96     kwargs['parents'] = [_PARSER]
97    
98     return argparse.ArgumentParser(**kwargs)
99    
100     def user(args):
101     user = os.environ['USER']
102    
103     if user == 'root':
104     user = args.user
105    
106     while USER.match(user) is None:
107     if args.user:
108     error(INVALID_USER % user)
109     elif user:
110     warn(INVALID_USER % user)
111    
112     user = raw_input('User: ')
113    
114     return user
115    
116     def warn(error):
117     sys.stderr.write('%s: %s\n' % (_program(), error))
118    
119 douglas 598 def adduser(user, name, password):
120     connection = ldap_connection()
121     uid = max(map(lambda user: int(user[1]['uidNumber'][0]), connection.search_s(PEOPLE, ldap.SCOPE_ONELEVEL, '(&(uid=*)(!(uid=root)))', ('uidNumber',)))) + 1
122     gid = uid
123     samba_gid = gid + 1000
124     home = os.path.join('/home', user)
125    
126     connection.add_s(_user(user), [
127     ('objectclass', ['top', 'account', 'posixAccount', 'shadowAccount', 'ucsbCcsLoginShells', 'sambaSamAccount']),
128     ('cn', name),
129     ('uid', user),
130     ('uidNumber', str(uid)),
131     ('gidNumber', str(gid)),
132     ('homeDirectory', home),
133     ('loginShell', 'bash'),
134     ] + zip(SHELLS, dict(common.SHELLS)['bash']) + [
135     ('sambaAcctFlags', '[U ]'),
136     ('sambaSID', SAMBA_SID % uid),
137     ('sambaPrimaryGroupSID', SAMBA_SID % samba_gid),
138     ])
139     connection.add_s(_group(user), [
140     ('objectclass', ['top', 'posixGroup', 'sambaGroupMapping']),
141     ('cn', user),
142     ('gidNumber', str(gid)),
143     ('sambaSID', SAMBA_SID % samba_gid),
144     ('sambaGroupType', '4'),
145     ])
146    
147     for group in ('wheel', 'fuse', 'operator'):
148     connection.modify_s(_group(group), [(ldap.MOD_ADD, 'memberUid', user)])
149    
150     connection.unbind_s()
151     os.umask(0022)
152     os.mkdir(home)
153     os.chown(home, uid, gid)
154    
155     for skel in ('/usr/share/skel', '/ccs/skel'):
156     for source, directories, files in os.walk(skel):
157     destination = os.path.join(home, source[len(skel):])
158    
159     for directory in directories:
160     target = os.path.join(destination, directory[3:] if directory.startswith('dot') else directory)
161    
162     os.mkdir(target)
163     shutil.copymode(os.path.join(source, directory), target)
164     os.chown(target, uid, gid)
165    
166     for file in files:
167     target = os.path.join(destination, file[3:] if file.startswith('dot') else file)
168    
169     shutil.copy(os.path.join(source, file), target)
170     os.chown(target, uid, gid)
171    
172     db = psycopg2.connect(database = 'postgres')
173     cursor = db.cursor()
174    
175     cursor.execute('create user %s with createdb' % user)
176     db.commit()
177    
178     passwd(user, None, password)
179    
180 douglas 592 def chfn(user, name):
181     connection = ldap_connection()
182    
183     connection.modify_s(_user(user), [(ldap.MOD_REPLACE, 'cn', name)])
184     connection.unbind_s()
185    
186 douglas 591 def chsh(user, shell, shells):
187     if shell != 'custom':
188     shells = dict(common.SHELLS)[shell]
189     else:
190     for _shell, _shells in common.SHELLS[:-1]:
191     if shells == _shells:
192     shell = _shell
193    
194     connection = ldap_connection()
195    
196     connection.modify_s(_user(user), map(lambda (key, value): (ldap.MOD_REPLACE, key, value), [('loginShell', shell)] + zip(SHELLS, shells)))
197     connection.unbind_s()
198    
199 douglas 585 def passwd(user, old_password, new_password):
200     connection = ldap_connection()
201    
202     connection.passwd_s(_user(user), old_password, new_password)
203     connection.unbind_s()
204    
205 douglas 606 with open(SECRET, 'rb') as secret:
206 douglas 585 db = MySQLdb.connect(passwd = secret.read(), db = 'mysql')
207    
208     cursor = db.cursor()
209    
210     cursor.execute('select count(User) from user where User = %s', (user,))
211    
212     if cursor.fetchone()[0]:
213     cursor.execute('update user set Password = PASSWORD(%s) where User = %s', (new_password, user))
214     cursor.execute('flush privileges');
215     else:
216     cursor.executemany('grant all on `' + db.escape_string(user) + r'\_%%`.* to %s@%s identified by %s', map(lambda host: (user, host, new_password), ('localhost', '%')))
217 douglas 600
218     if __name__ == '__main__':
219 douglas 681 parser = parser()
220     variables = ('MASTER', 'SLAVE', 'MASTER_URI', 'SLAVE_URI', 'BASE', 'PEOPLE', 'GROUP')
221 douglas 600
222 douglas 681 parser.add_argument('variables', nargs = '+', choices = variables)
223    
224     for variable in parser.parse_args().variables:
225     exec 'print %s' % variable

Properties

Name Value
svn:executable *
svn:keywords Id