~azzar1/unity/add-show-desktop-key

« back to all changes in this revision

Viewing changes to lib/common/makeuser.py

  • Committer: mattgiuca
  • Date: 2008-02-05 04:02:22 UTC
  • Revision ID: svn-v3-trunk0:2b9c9e99-6f39-0410-b283-7f802c844ae2:trunk:414
Added common/cgirequest.py. This takes the CGI environment and wraps it in a
Request object compatible with the IVLE request object (and our app
handlers).

Show diffs side-by-side

added added

removed removed

Lines of Context:
31
31
 
32
32
# TODO: When creating a new home directory, chown it to its owner
33
33
 
34
 
# TODO: In chown_to_webserver:
35
 
# Do not call os.system("chown www-data") - use Python lib
36
 
# and use the web server uid given in conf. (Several places).
37
 
 
38
 
import md5
39
34
import os
40
35
import stat
41
36
import shutil
42
 
import time
43
 
import uuid
44
37
import warnings
45
 
import filecmp
 
38
 
46
39
import conf
47
40
import db
48
41
 
49
 
def chown_to_webserver(filename):
50
 
    """
51
 
    Chowns a file so the web server user owns it.
52
 
    (This is useful in setting up Subversion conf files).
53
 
    Assumes root.
54
 
    """
55
 
    try:
56
 
        os.system("chown -R www-data:www-data %s" % filename)
57
 
    except:
58
 
        pass
59
 
 
60
 
def make_svn_repo(login, throw_on_error=True):
61
 
    """Create a repository for the given user.
62
 
    """
63
 
    path = os.path.join(conf.svn_repo_path, login)
64
 
    try:
65
 
        res = os.system("svnadmin create '%s'" % path)
66
 
        if res != 0 and throw_on_error:
67
 
            raise Exception("Cannot create repository for %s" % login)
68
 
    except Exception, exc:
69
 
        print repr(exc)
70
 
        if throw_on_error:
71
 
            raise
72
 
 
73
 
    chown_to_webserver(path)
74
 
 
75
 
def rebuild_svn_config():
76
 
    """Build the complete SVN configuration file.
77
 
    """
78
 
    conn = db.DB()
79
 
    res = conn.query("SELECT login, rolenm FROM login;").dictresult()
80
 
    groups = {}
81
 
    for r in res:
82
 
        role = r['rolenm']
83
 
        if role not in groups:
84
 
            groups[role] = []
85
 
        groups[role].append(r['login'])
86
 
    f = open(conf.svn_conf + ".new", "w")
87
 
    f.write("# IVLE SVN Repositories Configuration\n")
88
 
    f.write("# Auto-generated on %s\n" % time.asctime())
89
 
    f.write("\n")
90
 
    f.write("[groups]\n")
91
 
    for (g,ls) in groups.iteritems():
92
 
        f.write("%s = %s\n" % (g, ",".join(ls)))
93
 
    f.write("\n")
94
 
    for r in res:
95
 
        login = r['login']
96
 
        f.write("[%s:/]\n" % login)
97
 
        f.write("%s = rw\n" % login)
98
 
        #f.write("@tutor = r\n")
99
 
        #f.write("@lecturer = rw\n")
100
 
        #f.write("@admin = rw\n")
101
 
        f.write("\n")
102
 
    f.close()
103
 
    os.rename(conf.svn_conf + ".new", conf.svn_conf)
104
 
    chown_to_webserver(conf.svn_conf)
105
 
 
106
 
def make_svn_config(login, throw_on_error=True):
107
 
    """Add an entry to the apache-svn config file for the given user.
108
 
       Assumes the given user is either a guest or a student.
109
 
    """
110
 
    f = open(conf.svn_conf, "a")
111
 
    f.write("[%s:/]\n" % login)
112
 
    f.write("%s = rw\n" % login)
113
 
    #f.write("@tutor = r\n")
114
 
    #f.write("@lecturer = rw\n")
115
 
    #f.write("@admin = rw\n")
116
 
    f.write("\n")
117
 
    f.close()
118
 
    chown_to_webserver(conf.svn_conf)
119
 
 
120
 
def make_svn_auth(login, throw_on_error=True):
121
 
    """Setup svn authentication for the given user.
122
 
       FIXME: create local.auth entry
123
 
    """
124
 
    passwd = md5.new(uuid.uuid4().bytes).digest().encode('hex')
125
 
    if os.path.exists(conf.svn_auth_ivle):
126
 
        create = ""
127
 
    else:
128
 
        create = "c"
129
 
 
130
 
    db.DB().update_user(login, svn_pass=passwd)
131
 
 
132
 
    res = os.system("htpasswd -%smb %s %s %s" % (create,
133
 
                                              conf.svn_auth_ivle,
134
 
                                              login, passwd))
135
 
    if res != 0 and throw_on_error:
136
 
        raise Exception("Unable to create ivle-auth for %s" % login)
137
 
 
138
 
    # Make sure the file is owned by the web server
139
 
    if create == "c":
140
 
        chown_to_webserver(conf.svn_auth_ivle)
141
 
 
142
 
    return passwd
143
 
 
144
 
def generate_manifest(basedir, targetdir, parent=''):
145
 
    """ From a basedir and a targetdir work out which files are missing or out 
146
 
    of date and need to be added/updated and which files are redundant and need 
147
 
    to be removed.
148
 
    
149
 
    parent: This is used for the recursive call to track the relative paths 
150
 
    that we have decended.
151
 
    """
152
 
    
153
 
    cmp = filecmp.dircmp(basedir, targetdir)
154
 
 
155
 
    # Add all new files and files that have changed
156
 
    to_add = [os.path.join(parent,x) for x in (cmp.left_only + cmp.diff_files)]
157
 
 
158
 
    # Remove files that are redundant
159
 
    to_remove = [os.path.join(parent,x) for x in cmp.right_only]
160
 
    
161
 
    # Recurse
162
 
    for d in cmp.common_dirs:
163
 
        newbasedir = os.path.join(basedir, d)
164
 
        newtargetdir = os.path.join(targetdir, d)
165
 
        newparent = os.path.join(parent, d)
166
 
        (sadd,sremove) = generate_manifest(newbasedir, newtargetdir, newparent)
167
 
        to_add += sadd
168
 
        to_remove += sremove
169
 
 
170
 
    return (to_add, to_remove)
171
 
 
172
 
 
173
 
def make_jail(username, uid, force=True, manifest=None, svn_pass=None):
 
42
def make_jail(username, uid, force=True):
174
43
    """Creates a new user's jail space, in the jail directory as configured in
175
44
    conf.py.
176
45
 
177
 
    This expects there to be a "staging" directory within the jail root which
 
46
    This expects there to be a "template" directory within the jail root which
178
47
    contains all the files for a sample student jail. It creates the student's
179
48
    directory in the jail root, by making a hard-link copy of every file in the
180
 
    staging directory, recursively.
 
49
    template directory, recursively.
181
50
 
182
51
    Returns the path to the user's home directory.
183
52
 
190
59
 
191
60
    force: If false, exception if jail already exists for this user.
192
61
    If true (default), overwrites it, but preserves home directory.
193
 
 
194
 
    manifest: If provided this will be a pair (to_add, to_remove) of files or
195
 
    directories to add or remove from the jail.
196
 
 
197
 
    svn_pass: If provided this will be a string, the randomly-generated
198
 
    Subversion password for this user (if you happen to already have it).
199
 
    If not provided, it will be read from the database.
200
62
    """
201
63
    # MUST run as root or some of this may fail
202
64
    if os.getuid() != 0:
203
65
        raise Exception("Must run make_jail as root")
204
66
    
205
 
    stagingdir = os.path.join(conf.jail_base, '__staging__')
206
 
    if not os.path.isdir(stagingdir):
207
 
        raise Exception("Staging jail directory does not exist: " +
208
 
            stagingdir)
 
67
    templatedir = os.path.join(conf.jail_base, 'template')
 
68
    if not os.path.isdir(templatedir):
 
69
        raise Exception("Template jail directory does not exist: " +
 
70
            templatedir)
209
71
    # tempdir is for putting backup homes in
210
 
    tempdir = os.path.join(conf.jail_base, '__temp__')
 
72
    tempdir = os.path.join(conf.jail_base, 'temp')
211
73
    if not os.path.exists(tempdir):
212
74
        os.makedirs(tempdir)
213
75
    elif not os.path.isdir(tempdir):
234
96
            # the backup will be un-made.
235
97
            # XXX This will still leave the user's jail in an unusable state,
236
98
            # but at least they won't lose their files.
237
 
            if manifest:
238
 
                (to_add, to_remove) = manifest
239
 
                # Remove redundant files and directories
240
 
                for d in to_remove:
241
 
                    dst = os.path.join(userdir, d)
242
 
                    src = os.path.join(stagingdir, d)
243
 
                    if os.path.isdir(dst):
244
 
                        shutil.rmtree(dst)
245
 
                    elif os.path.isfile(dst):
246
 
                        os.remove(dst)
247
 
                # Add new files
248
 
                for d in to_add:
249
 
                    dst = os.path.join(userdir, d)
250
 
                    src = os.path.join(stagingdir, d)
251
 
                    # Clear the previous file/dir
252
 
                    if os.path.isdir(dst):
253
 
                        shutil.rmtree(dst)
254
 
                    elif os.path.isfile(dst):
255
 
                        os.remove(dst)
256
 
                    # Link the file/dirs
257
 
                    if os.path.isdir(src):
258
 
                        linktree(src, dst)
259
 
                    elif os.path.isfile(src):
260
 
                        os.link(src, dst)
261
 
                    
262
 
            else:
263
 
                # No manifest, do a full rebuild
264
 
                shutil.rmtree(userdir)
265
 
                # Hard-link (copy aliasing) the entire tree over
266
 
                linktree(stagingdir, userdir)
 
99
            shutil.rmtree(userdir)
 
100
 
 
101
            # Hard-link (copy aliasing) the entire tree over
 
102
            linktree(templatedir, userdir)
267
103
        finally:
268
104
            # Set up the user's home directory (restore backup)
269
105
            # First make sure the directory is empty and its parent exists
275
111
            # directory). But it shouldn't fail as homedir should not exist.
276
112
            os.makedirs(homedir)
277
113
            shutil.move(homebackup, homedir)
278
 
        userhomedir = os.path.join(homedir, username)   # Return value
 
114
        return os.path.join(homedir, username)
279
115
    else:
280
116
        # No user jail exists
281
117
        # Hard-link (copy aliasing) the entire tree over
282
 
        linktree(stagingdir, userdir)
 
118
        linktree(templatedir, userdir)
283
119
 
284
120
        # Set up the user's home directory
285
121
        userhomedir = os.path.join(homedir, username)
286
122
        os.mkdir(userhomedir)
287
123
        # Chown (and set the GID to the same as the UID).
288
124
        os.chown(userhomedir, uid, uid)
289
 
        # Chmod to rwxr-xr-x (755)
290
 
        os.chmod(userhomedir, 0755)
291
 
 
292
 
    # There is 1 special file which should not be hard-linked, but instead
293
 
    # generated specific to this user: /opt/ivle/lib/conf/conf.py.
294
 
    # "__" username "__" users are exempt (special)
295
 
    if not (username.startswith("__") and username.endswith("__")):
296
 
        make_conf_py(username, userdir, stagingdir, svn_pass)
297
 
 
298
 
    return userhomedir
299
 
 
300
 
def make_conf_py(username, user_jail_dir, staging_dir, svn_pass=None):
301
 
    """
302
 
    Creates (overwriting any existing file) a file /opt/ivle/lib/conf/conf.py
303
 
    in a given user's jail.
304
 
    username: Username.
305
 
    user_jail_dir: User's jail dir, ie. conf.jail_base + username
306
 
    staging_dir: The dir with the staging copy of the jail. (With the
307
 
        template conf.py file).
308
 
    svn_pass: As with make_jail. User's SVN password, but if not supplied,
309
 
        will look up in the DB.
310
 
    """
311
 
    # Note: It is important to delete this file and recreate it (somewhat
312
 
    # ironically beginning by pasting the same contents in again), rather than
313
 
    # simply appending.
314
 
    # Note that all files initially are aliased, so appending would result
315
 
    # in a massive aliasing problem. Deleting and recreating ensures that
316
 
    # the conf.py files are unique to each jail.
317
 
    template_conf_path = os.path.join(staging_dir,"opt/ivle/lib/conf/conf.py")
318
 
    conf_path = os.path.join(user_jail_dir, "opt/ivle/lib/conf/conf.py")
319
 
 
320
 
    # If svn_pass isn't supplied, grab it from the DB
321
 
    if svn_pass is None:
322
 
        dbconn = db.DB()
323
 
        svn_pass = dbconn.get_user(username).svn_pass
324
 
        dbconn.close()
325
 
 
326
 
    # Read the contents of the template conf file
327
 
    try:
328
 
        template_conf_file = open(template_conf_path, "r")
329
 
        template_conf_data = template_conf_file.read()
330
 
        template_conf_file.close()
331
 
    except:
332
 
        # Couldn't open template conf.py for some reason
333
 
        # Just treat it as empty file
334
 
        template_conf_data = ""
335
 
 
336
 
    # Remove the target conf file if it exists
337
 
    try:
338
 
        os.remove(conf_path)
339
 
    except OSError:
340
 
        pass
341
 
    conf_file = open(conf_path, "w")
342
 
    conf_file.write(template_conf_data)
343
 
    conf_file.write("\n# The login name for the owner of the jail\n")
344
 
    conf_file.write("login = %s\n" % repr(username))
345
 
    conf_file.write("\n")
346
 
    conf_file.write("# The subversion-only password for the owner of "
347
 
        "the jail\n")
348
 
    conf_file.write("svn_pass = %s\n" % repr(svn_pass))
349
 
    conf_file.close()
350
 
 
351
 
    # Make this file world-readable
352
 
    # (chmod 644 conf_path)
353
 
    os.chmod(conf_path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP
354
 
                        | stat.S_IROTH)
 
125
        # Chmod to rwx------ (700)
 
126
        os.chmod(userhomedir, stat.S_IRWXU)
 
127
        return userhomedir
355
128
 
356
129
def linktree(src, dst):
357
130
    """Recursively hard-link a directory tree using os.link().
392
165
    if errors:
393
166
        raise Exception, errors
394
167
 
395
 
def make_user_db(throw_on_error = True, **kwargs):
 
168
def make_user_db(login, password, nick, fullname, rolenm, studentid,
 
169
    force=True):
396
170
    """Creates a user's entry in the database, filling in all the fields.
397
 
    All arguments must be keyword args. They are the fields in the table.
398
 
    However, instead of supplying a "passhash", you must supply a
399
 
    "password" argument, which will be hashed internally.
400
 
    Also do not supply a state. All users are created in the "no_agreement"
401
 
    state.
402
 
    Throws an exception if the user already exists.
 
171
    If force is False, throws an exception if the user already exists.
 
172
    If True, overwrites the user's entry in the DB.
403
173
    """
404
174
    dbconn = db.DB()
405
 
    dbconn.create_user(**kwargs)
 
175
    if force:
 
176
        # Delete user if it exists
 
177
        try:
 
178
            dbconn.delete_user(login)
 
179
        except:
 
180
            pass
 
181
    dbconn.create_user(login, password, nick, fullname, rolenm, studentid)
406
182
    dbconn.close()
407
 
 
408
 
    if kwargs['password']:
409
 
        if os.path.exists(conf.svn_auth_local):
410
 
            create = ""
411
 
        else:
412
 
            create = "c"
413
 
        res = os.system("htpasswd -%smb %s %s %s" % (create,
414
 
                                                     conf.svn_auth_local,
415
 
                                                     kwargs['login'],
416
 
                                                     kwargs['password']))
417
 
        if res != 0 and throw_on_error:
418
 
            raise Exception("Unable to create local-auth for %s" % kwargs['login'])
419
 
 
420
 
    # Make sure the file is owned by the web server
421
 
    if create == "c":
422
 
        chown_to_webserver(conf.svn_auth_local)