8
from functools import partial
16
import common.makeuser
17
import common.studpath
20
# usrmgt-server <port> <magic>
22
# User management operations:
24
# - [Re]Create jail for a user
25
# - Create a svn repository for a user
29
# - Checkout repository as home directory
31
# - Disable a user's account
32
# - Enable a user's account
34
# - Rebuild svn config
35
# - Rebuild svn auth file
36
# - Rebuild passwd + push to nodes.
38
def create_user(props):
39
"""Create the database record for the given user.
41
login - used as a unix login name and svn repository name.
43
unixid - the unix uid under which execution will take place
44
on the behalf of the user. Don't use 0! If not specified
45
or None, one will be allocated from the configured
48
password - the clear-text password for the user. If this property is
49
absent or None, this is an indication that external
50
authentication should be used (i.e. LDAP).
52
email - the user's email address.
54
nick - the display name to use.
56
fullname - The name of the user for results and/or other official
59
rolenm - The user's role. Must be one of "anyone", "student",
60
"tutor", "lecturer", "admin".
62
studentid - If supplied and not None, the student id of the user for
63
results and/or other official purposes.
65
Return Value: the uid associated with the user. INT
68
if 'unixid' not in props or props['unixid'] is None:
69
# For the time being, just choose a random unixid.
70
# This may result in duplicates, but the unixid is essentially
71
# a monitoring/logging convenience only. Oh, and dups could kill
73
props['unixid'] = random.randrange(5000,10000)
74
# raise NotImplementedError, "No algorithm for creating uids yet!"
76
# props['unixid'] = unixid
78
common.makeuser.make_user_db(**props)
80
return int(props['unixid'])
82
def update_user(props):
83
"""Create the database record for the given user.
85
login - user who is making the change (not necessarily the one
87
update - dict of fields to be updated. The fields are all those
88
in the login table of the db.
90
Omitted fields will not be set.
92
update = props['update']
93
# Note: "login" is special - it is not part of the kwargs to
94
# db.update_user, but the first arg - used to find the user to update.
95
# However it doesn't need any special treatment here.
98
db.update_user(**update)
101
def get_login(login, passwd, _realm, _login, _may_save):
102
"""Callback function used by pysvn for authentication.
104
logging.debug("Getting password for %s (realm %s)" % (login, _realm))
105
return (True, login, passwd, False)
107
def activate_user(props):
108
"""Create the on-disk stuff for the given user.
109
Sets the state of the user in the db from pending to enabled.
111
login - the user name for the jail
116
login = props['login']
122
# FIXME: check we're pending
124
details = db.get_user(login)
126
# make svn config/auth
128
logging.debug("Creating repo")
129
common.makeuser.make_svn_repo(login, throw_on_error=False)
130
logging.debug("Creating svn config")
131
common.makeuser.make_svn_config(login, throw_on_error=False)
132
logging.debug("Creating svn auth")
133
passwd = common.makeuser.make_svn_auth(login, throw_on_error=False)
134
logging.debug("passwd: %s" % passwd)
137
svn.callback_get_login = partial(get_login, login, passwd)
139
if conf.svn_addr[-1] != '/':
140
conf.svn_addr = conf.svn_addr + "/"
142
# FIXME: This should be a loop over enrolements.
143
# We're not going to fix this now because it requires
144
# a large amount of admin console work to manage subject
146
# Instead, we're just going to use a single offering with
147
# a "short" name of "info1".
148
logging.debug("Creating /info1")
150
svn.mkdir(conf.svn_addr + login + "/info1",
151
"Initial creation of work directory for Informatics 1")
152
except Exception, exc:
153
logging.warning("While mkdiring info1: %s" % str(exc))
155
logging.debug("Creating /stuff")
157
svn.mkdir(conf.svn_addr + login + "/stuff",
158
"Initial creation of directory for miscellania")
159
except Exception, exc:
160
logging.warning("While mkdiring stuff: %s" % str(exc))
163
logging.debug("Creating jail")
164
common.makeuser.make_jail(login, details.unixid)
168
tcf_path = os.path.join(conf.jail_base, 'template/opt/ivle/lib/conf/conf.py')
169
cf_path = os.path.join(conf.jail_base, login, 'opt/ivle/lib/conf/conf.py')
172
cf = open(cf_path, "w")
173
cf.write(open(tcf_path, "r").read())
174
cf.write("# The login name for the owner of the jail\n")
175
cf.write("login = %s\n" % repr(login))
177
cf.write("# The subversion-only password for the owner of the jail\n")
178
cf.write("svn_pass = %s\n" % repr(passwd))
181
# Check out the repositories into the student's home dir
182
# FIXME: Call do_checkout instead of replicating this code.
183
# (do_checkout currently doesn't work)
188
logging.debug("Checking out directories in the jail")
190
svn.checkout(conf.svn_addr + login + "/stuff",
191
common.studpath.url_to_local(login + "/stuff")[1])
192
os.system("chown -R %d:%d %s" \
193
% (details.unixid, details.unixid,
194
common.studpath.url_to_local(login + "/stuff")[1]))
195
except Exception, exc:
196
logging.warning("While mkdiring stuff: %s" % str(exc))
199
svn.checkout(conf.svn_addr + login + "/info1",
200
common.studpath.url_to_local(login + "/info1")[1])
201
os.system("chown -R %d:%d %s" \
202
% (details.unixid, details.unixid,
203
common.studpath.url_to_local(login + "/info1")[1]))
204
except Exception, exc:
205
logging.warning("While mkdiring info1: %s" % str(exc))
208
# FIXME: should this be nicer?
209
os.system("chown -R %d:%d %s" \
210
% (details.unixid, details.unixid,
211
common.studpath.url_to_local(login)[1]))
214
logging.info("Enabling user")
215
db.update_user(login, state='enabled')
217
return {"response": "okay"}
222
def do_checkout(props):
223
"""Create the default contents of a user's jail by checking out from the
225
If any directory already exists, just fail silently (so this is not a
226
"wipe-and-checkout", merely a checkout if it failed or something).
228
login - the user name for the jail
232
# XXX / FIXME: This doesn't work - cannot get passwd
233
# (activate_me creates passwd, other svn stuff gets it from jailconf - we
234
# can't access either at this point).
236
login = props['login']
238
svn.callback_get_login = partial(get_login, login, passwd)
240
if conf.svn_addr[-1] != '/':
241
conf.svn_addr = conf.svn_addr + "/"
245
logging.debug("Checking out directories in the jail")
247
svn.checkout(conf.svn_addr + login + "/stuff",
248
common.studpath.url_to_local(login + "/stuff")[1])
249
os.system("chown -R %d:%d %s" \
250
% (details.unixid, details.unixid,
251
common.studpath.url_to_local(login + "/stuff")[1]))
252
except Exception, exc:
253
logging.warning("While mkdiring stuff: %s" % str(exc))
256
svn.checkout(conf.svn_addr + login + "/info1",
257
common.studpath.url_to_local(login + "/info1")[1])
258
os.system("chown -R %d:%d %s" \
259
% (details.unixid, details.unixid,
260
common.studpath.url_to_local(login + "/info1")[1]))
261
except Exception, exc:
262
logging.warning("While mkdiring info1: %s" % str(exc))
265
return {"response": "okay"}
268
'create_user':create_user,
269
'update_user':update_user,
270
'activate_user':activate_user,
271
'do_checkout':do_checkout,
276
pidfile = open('/var/run/usrmgt-server.pid', 'w')
277
pidfile.write('%d\n' % os.getpid())
279
except IOError, (errno, strerror):
280
print "Couldn't write PID file. IO error(%s): %s" % (errno, strerror)
284
logging.debug(repr(props))
285
action = props.keys()[0]
286
return actions[action](props[action])
288
if __name__ == "__main__":
290
print >>sys.stderr, "Usage: usrmgt-server <port> <magic>"
292
port = int(sys.argv[1])
297
logging.basicConfig(filename="/var/log/usrmgt.log", level=logging.INFO)
298
logging.info("Starting usrmgt server on port %d (pid = %d)" % (port, pid))
300
common.chat.start_server(port, magic, True, dispatch, initializer)