35
25
# - Rebuild svn auth file
36
26
# - Rebuild passwd + push to nodes.
38
29
def create_user(props):
39
30
"""Create the database record for the given user.
40
31
Expected properties:
41
login - used as a unix login name and svn repository name.
32
username - used as a unix login name and svn repository name.
43
unixid - the unix uid under which execution will take place
34
uid - the unix uid under which execution will take place
44
35
on the behalf of the user. Don't use 0! If not specified
45
36
or None, one will be allocated from the configured
65
56
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, existing_login, _may_save):
102
"""Callback function used by pysvn for authentication.
103
login, passwd: Credentials of the user attempting to log in.
104
passwd in clear (this should be the user's svn_pass, a
105
randomly-generated permanent password for this user, not their main
107
realm, existing_login, _may_save: The 3 arguments passed by pysvn to
109
The following has been determined empirically, not from docs:
110
existing_login will be the name of the user who owns the process on
111
the first attempt, "" on subsequent attempts. We use this fact.
113
logging.debug("Getting password for %s (realm %s)" % (login, realm))
114
# Only provide credentials on the _first_ attempt.
115
# If we're being asked again, then it means the credentials failed for
116
# some reason and we should just fail. (This is not desirable, but it's
117
# better than being asked an infinite number of times).
118
if existing_login == "":
119
logging.warning("Could not authenticate SVN for %s" % login)
120
return (False, login, passwd, False)
122
return (True, login, passwd, False)
59
# FIXME: the IVLE server must check that an admin is doing this!
61
if 'uid' not in props or props['uid'] is None:
62
raise NotImplementedError, "No algorithm for creating uids yet!"
66
username = props['username']
69
password = props['password']
73
email = props['email']
77
fullname = props['fullname']
78
rolenm = props['rolenm']
80
studentid = props['studentid']
83
common.makeuser.make_user_db(username, uid, password, email, nick,
84
fullname, rolenm, studentid)
124
88
def activate_user(props):
125
89
"""Create the on-disk stuff for the given user.
143
105
details = db.get_user(login)
145
# make svn config/auth
147
logging.debug("Creating repo")
148
common.makeuser.make_svn_repo(login, throw_on_error=False)
149
logging.debug("Creating svn config")
150
common.makeuser.make_svn_config(login, throw_on_error=False)
151
logging.debug("Creating svn auth")
152
passwd = common.makeuser.make_svn_auth(login, throw_on_error=False)
153
logging.debug("passwd: %s" % passwd)
156
svn.callback_get_login = partial(get_login, login, passwd)
158
if conf.svn_addr[-1] != '/':
159
conf.svn_addr = conf.svn_addr + "/"
161
# FIXME: This should be a loop over enrolements.
162
# We're not going to fix this now because it requires
163
# a large amount of admin console work to manage subject
165
# Instead, we're just going to use a single offering with
166
# a "short" name of "info1".
167
logging.debug("Creating /info1")
169
svn.mkdir(conf.svn_addr + login + "/info1",
170
"Initial creation of work directory for Informatics 1")
171
except Exception, exc:
172
logging.warning("While mkdiring info1: %s" % str(exc))
174
logging.debug("Creating /stuff")
176
svn.mkdir(conf.svn_addr + login + "/stuff",
177
"Initial creation of directory for miscellania")
178
except Exception, exc:
179
logging.warning("While mkdiring stuff: %s" % str(exc))
182
logging.debug("Creating jail")
183
common.makeuser.make_jail(login, details.unixid, svn_pass=passwd)
185
common.makeuser.mount_jail(login)
187
do_checkout(props, svn_pass=passwd)
189
# FIXME: should this be nicer?
190
os.system("chown -R %d:%d %s" \
191
% (details.unixid, details.unixid,
192
common.studpath.url_to_local(login)[1]))
194
logging.info("Enabling user")
107
# FIXME: make svn config/auth
111
common.makeuser.make_jail(login, details['unixid'])
195
113
db.update_user(login, state='enabled')
197
115
return {"response": "okay"}
202
def do_checkout(props, svn_pass=None):
203
"""Create the default contents of a user's jail by checking out from the
205
If any directory already exists, just fail silently (so this is not a
206
"wipe-and-checkout", merely a checkout if it failed or something).
208
login - the user name for the jail
212
login = props['login']
215
user = db.get_user(login)
216
# If svn_pass isn't supplied, grab it from the DB
218
svn_pass = user.svn_pass
222
svn.callback_get_login = partial(get_login, login, svn_pass)
224
if conf.svn_addr[-1] != os.sep:
225
conf.svn_addr += os.sep
229
logging.debug("Checking out directories in the jail")
231
svn.checkout(conf.svn_addr + login + "/stuff",
232
common.studpath.url_to_local(login + "/stuff")[1])
233
os.system("chown -R %d:%d %s" \
234
% (user.unixid, user.unixid,
235
common.studpath.url_to_local(login + "/stuff")[1]))
236
except Exception, exc:
237
logging.warning("While mkdiring stuff: %s" % str(exc))
240
svn.checkout(conf.svn_addr + login + "/info1",
241
common.studpath.url_to_local(login + "/info1")[1])
242
os.system("chown -R %d:%d %s" \
243
% (user.unixid, user.unixid,
244
common.studpath.url_to_local(login + "/info1")[1]))
245
except Exception, exc:
246
logging.warning("While mkdiring info1: %s" % str(exc))
251
return {"response": "okay"}
254
121
'create_user':create_user,
255
'update_user':update_user,
256
'activate_user':activate_user,
257
'do_checkout':do_checkout,
122
'activate_user':activate_user
262
pidfile = open('/var/run/usrmgt-server.pid', 'w')
263
pidfile.write('%d\n' % os.getpid())
265
except IOError, (errno, strerror):
266
print "Couldn't write PID file. IO error(%s): %s" % (errno, strerror)
269
125
def dispatch(props):
270
logging.debug(repr(props))
271
126
action = props.keys()[0]
272
127
return actions[action](props[action])
274
129
if __name__ == "__main__":
276
print >>sys.stderr, "Usage: usrmgt-server <port> <magic>"
278
130
port = int(sys.argv[1])
279
131
magic = sys.argv[2]
283
logging.basicConfig(filename="/var/log/usrmgt.log", level=logging.INFO)
284
logging.info("Starting usrmgt server on port %d (pid = %d)" % (port, pid))
286
common.chat.start_server(port, magic, True, dispatch, initializer)
133
common.chat.start_server(port, magic, False, dispatch)