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

« back to all changes in this revision

Viewing changes to lib/common/makeuser.py

  • Committer: dcoles
  • Date: 2008-07-03 04:20:54 UTC
  • Revision ID: svn-v3-trunk0:2b9c9e99-6f39-0410-b283-7f802c844ae2:trunk:803
Setup: Modularised setup.py so it is now no longer over 1000 lines. This should 
allow us to get in there and tidy up each module much easier. Also removed 
updatejails since this functionality seems to be duplicated with remakeuser.py 
and remakealluser.py scripts.

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
 
34
38
import md5
35
39
import os
36
40
import stat
38
42
import time
39
43
import uuid
40
44
import warnings
41
 
 
 
45
import filecmp
42
46
import conf
43
47
import db
44
48
 
45
 
def make_svn_repo(login):
 
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):
46
61
    """Create a repository for the given user.
47
62
    """
48
63
    path = os.path.join(conf.svn_repo_path, login)
49
 
    res = os.system("svnadmin create '%s'" % path)
50
 
    if res != 0:
51
 
        raise Exception("Cannot create repository for %s" % 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)
52
74
 
53
75
def rebuild_svn_config():
54
76
    """Build the complete SVN configuration file.
73
95
        login = r['login']
74
96
        f.write("[%s:/]\n" % login)
75
97
        f.write("%s = rw\n" % login)
76
 
        f.write("@tutor = r\n")
77
 
        f.write("@lecturer = rw\n")
78
 
        f.write("@admin = rw\n")
 
98
        #f.write("@tutor = r\n")
 
99
        #f.write("@lecturer = rw\n")
 
100
        #f.write("@admin = rw\n")
79
101
        f.write("\n")
80
102
    f.close()
81
103
    os.rename(conf.svn_conf + ".new", conf.svn_conf)
 
104
    chown_to_webserver(conf.svn_conf)
82
105
 
83
 
def make_svn_config(login):
 
106
def make_svn_config(login, throw_on_error=True):
84
107
    """Add an entry to the apache-svn config file for the given user.
85
108
       Assumes the given user is either a guest or a student.
86
109
    """
87
110
    f = open(conf.svn_conf, "a")
88
111
    f.write("[%s:/]\n" % login)
89
112
    f.write("%s = rw\n" % login)
90
 
    f.write("@tutor = r\n")
91
 
    f.write("@lecturer = rw\n")
92
 
    f.write("@admin = rw\n")
 
113
    #f.write("@tutor = r\n")
 
114
    #f.write("@lecturer = rw\n")
 
115
    #f.write("@admin = rw\n")
93
116
    f.write("\n")
94
117
    f.close()
 
118
    chown_to_webserver(conf.svn_conf)
95
119
 
96
 
def make_svn_auth(login):
 
120
def make_svn_auth(login, throw_on_error=True):
97
121
    """Setup svn authentication for the given user.
98
122
       FIXME: create local.auth entry
99
123
    """
103
127
    else:
104
128
        create = "c"
105
129
 
106
 
    db.DB().update_user({'svn_pass':passwd})
 
130
    db.DB().update_user(login, svn_pass=passwd)
107
131
 
108
 
    res = os.system("htpasswd -%smb %s %s" % (create,
 
132
    res = os.system("htpasswd -%smb %s %s %s" % (create,
109
133
                                              conf.svn_auth_ivle,
110
134
                                              login, passwd))
111
 
    if res != 0:
 
135
    if res != 0 and throw_on_error:
112
136
        raise Exception("Unable to create ivle-auth for %s" % login)
113
137
 
114
 
def make_jail(username, uid, force=True):
 
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):
115
174
    """Creates a new user's jail space, in the jail directory as configured in
116
175
    conf.py.
117
176
 
118
 
    This expects there to be a "template" directory within the jail root which
 
177
    This expects there to be a "staging" directory within the jail root which
119
178
    contains all the files for a sample student jail. It creates the student's
120
179
    directory in the jail root, by making a hard-link copy of every file in the
121
 
    template directory, recursively.
 
180
    staging directory, recursively.
122
181
 
123
182
    Returns the path to the user's home directory.
124
183
 
131
190
 
132
191
    force: If false, exception if jail already exists for this user.
133
192
    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.
134
200
    """
135
201
    # MUST run as root or some of this may fail
136
202
    if os.getuid() != 0:
137
203
        raise Exception("Must run make_jail as root")
138
204
    
139
 
    templatedir = os.path.join(conf.jail_base, 'template')
140
 
    if not os.path.isdir(templatedir):
141
 
        raise Exception("Template jail directory does not exist: " +
142
 
            templatedir)
 
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)
143
209
    # tempdir is for putting backup homes in
144
 
    tempdir = os.path.join(conf.jail_base, 'temp')
 
210
    tempdir = os.path.join(conf.jail_base, '__temp__')
145
211
    if not os.path.exists(tempdir):
146
212
        os.makedirs(tempdir)
147
213
    elif not os.path.isdir(tempdir):
168
234
            # the backup will be un-made.
169
235
            # XXX This will still leave the user's jail in an unusable state,
170
236
            # but at least they won't lose their files.
171
 
            shutil.rmtree(userdir)
172
 
 
173
 
            # Hard-link (copy aliasing) the entire tree over
174
 
            linktree(templatedir, userdir)
 
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)
175
267
        finally:
176
268
            # Set up the user's home directory (restore backup)
177
269
            # First make sure the directory is empty and its parent exists
183
275
            # directory). But it shouldn't fail as homedir should not exist.
184
276
            os.makedirs(homedir)
185
277
            shutil.move(homebackup, homedir)
186
 
        return os.path.join(homedir, username)
 
278
        userhomedir = os.path.join(homedir, username)   # Return value
187
279
    else:
188
280
        # No user jail exists
189
281
        # Hard-link (copy aliasing) the entire tree over
190
 
        linktree(templatedir, userdir)
 
282
        linktree(stagingdir, userdir)
191
283
 
192
284
        # Set up the user's home directory
193
285
        userhomedir = os.path.join(homedir, username)
196
288
        os.chown(userhomedir, uid, uid)
197
289
        # Chmod to rwxr-xr-x (755)
198
290
        os.chmod(userhomedir, 0755)
199
 
        return userhomedir
 
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 = ("# Warning: Problem building config script.\n"
 
335
                              "# Could not find template conf.py file.\n")
 
336
 
 
337
    # Remove the target conf file if it exists
 
338
    try:
 
339
        os.remove(conf_path)
 
340
    except OSError:
 
341
        pass
 
342
    conf_file = open(conf_path, "w")
 
343
    conf_file.write(template_conf_data)
 
344
    conf_file.write("\n# The login name for the owner of the jail\n")
 
345
    conf_file.write("login = %s\n" % repr(username))
 
346
    conf_file.write("\n")
 
347
    conf_file.write("# The subversion-only password for the owner of "
 
348
        "the jail\n")
 
349
    conf_file.write("svn_pass = %s\n" % repr(svn_pass))
 
350
    conf_file.close()
 
351
 
 
352
    # Make this file world-readable
 
353
    # (chmod 644 conf_path)
 
354
    os.chmod(conf_path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP
 
355
                        | stat.S_IROTH)
200
356
 
201
357
def linktree(src, dst):
202
358
    """Recursively hard-link a directory tree using os.link().
237
393
    if errors:
238
394
        raise Exception, errors
239
395
 
240
 
def make_user_db(**kwargs):
 
396
def make_user_db(throw_on_error = True, **kwargs):
241
397
    """Creates a user's entry in the database, filling in all the fields.
242
398
    All arguments must be keyword args. They are the fields in the table.
243
399
    However, instead of supplying a "passhash", you must supply a
249
405
    dbconn = db.DB()
250
406
    dbconn.create_user(**kwargs)
251
407
    dbconn.close()
 
408
 
 
409
    if kwargs['password']:
 
410
        if os.path.exists(conf.svn_auth_local):
 
411
            create = ""
 
412
        else:
 
413
            create = "c"
 
414
        res = os.system("htpasswd -%smb %s %s %s" % (create,
 
415
                                                     conf.svn_auth_local,
 
416
                                                     kwargs['login'],
 
417
                                                     kwargs['password']))
 
418
        if res != 0 and throw_on_error:
 
419
            raise Exception("Unable to create local-auth for %s" % kwargs['login'])
 
420
 
 
421
    # Make sure the file is owned by the web server
 
422
    if create == "c":
 
423
        chown_to_webserver(conf.svn_auth_local)