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

1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
1
# IVLE - Informatics Virtual Learning Environment
2
# Copyright (C) 2007-2008 The University of Melbourne
3
#
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation; either version 2 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU General Public License for more details.
13
#
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, write to the Free Software
16
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17
18
# Module: MakeUser
19
# Author: Matt Giuca
20
# Date:   1/2/2008
21
22
# Allows creation of users. This sets up the following:
23
# * User's jail and home directory within the jail.
24
# * Subversion repository (TODO)
25
# * Check out Subversion workspace into jail (TODO)
26
# * Database details for user
27
# * Unix user account
28
29
# TODO: Sanitize login name and other fields.
30
# Users must not be called "temp" or "template".
31
32
# TODO: When creating a new home directory, chown it to its owner
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
1197 by Matt Giuca
ivle.chat, ivle.database, ivle.makeuser: Replaced use of md5 library with
38
import hashlib
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
39
import os
40
import stat
41
import shutil
42
import time
43
import uuid
44
import warnings
45
import filecmp
46
import logging
47
import ivle.pulldown_subj
48
1080.1.44 by William Grant
ivle.makeuser: Port rebuild_svn_group_config() to Storm.
49
from ivle.database import ProjectGroup
50
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
51
def chown_to_webserver(filename):
52
    """
53
    Chowns a file so the web server user owns it.
54
    (This is useful in setting up Subversion conf files).
55
    Assumes root.
56
    """
57
    try:
58
        os.system("chown -R www-data:www-data %s" % filename)
59
    except:
60
        pass
61
62
def make_svn_repo(path, throw_on_error=True):
63
    """Create a Subversion repository at the given path.
64
    """
65
    try:
66
        res = os.system("svnadmin create '%s'" % path)
67
        if res != 0 and throw_on_error:
68
            raise Exception("Cannot create repository: %s" % path)
69
    except Exception, exc:
70
        print repr(exc)
71
        if throw_on_error:
72
            raise
73
74
    chown_to_webserver(path)
75
1229 by Matt Giuca
ivle.makeuser: rebuild_svn_config and rebuild_svn_group_config now require a
76
def rebuild_svn_config(store, config):
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
77
    """Build the complete SVN configuration file.
1229 by Matt Giuca
ivle.makeuser: rebuild_svn_config and rebuild_svn_group_config now require a
78
    @param config: An ivle.config.Config object.
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
79
    """
1080.1.17 by me at id
ivle.makeuser: svn auth manipulation functions now use storm as much as
80
    users = store.find(ivle.database.User)
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
81
    groups = {}
1101 by William Grant
Privileges (apart from admin) are now offering-local, not global.
82
    # TODO: Populate groups with per-offering tutors/lecturers/etc.
1229 by Matt Giuca
ivle.makeuser: rebuild_svn_config and rebuild_svn_group_config now require a
83
    conf_name = config['paths']['svn']['conf']
84
    temp_name = conf_name + ".new"
85
    f = open(temp_name, "w")
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
86
    f.write("# IVLE SVN Repositories Configuration\n")
87
    f.write("# Auto-generated on %s\n" % time.asctime())
88
    f.write("\n")
89
    f.write("[groups]\n")
90
    for (g,ls) in groups.iteritems():
91
        f.write("%s = %s\n" % (g, ",".join(ls)))
92
    f.write("\n")
93
    for u in users:
94
        f.write("[%s:/]\n" % u.login)
95
        f.write("%s = rw\n" % u.login)
96
        #f.write("@tutor = r\n")
97
        #f.write("@lecturer = rw\n")
98
        #f.write("@admin = rw\n")
99
        f.write("\n")
100
    f.close()
1229 by Matt Giuca
ivle.makeuser: rebuild_svn_config and rebuild_svn_group_config now require a
101
    os.rename(temp_name, conf_name)
102
    chown_to_webserver(conf_name)
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
103
1229 by Matt Giuca
ivle.makeuser: rebuild_svn_config and rebuild_svn_group_config now require a
104
def rebuild_svn_group_config(store, config):
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
105
    """Build the complete SVN configuration file for groups
1229 by Matt Giuca
ivle.makeuser: rebuild_svn_config and rebuild_svn_group_config now require a
106
    @param config: An ivle.config.Config object.
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
107
    """
1229 by Matt Giuca
ivle.makeuser: rebuild_svn_config and rebuild_svn_group_config now require a
108
    conf_name = config['paths']['svn']['group_conf']
109
    temp_name = conf_name + ".new"
110
    f = open(temp_name, "w")
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
111
    f.write("# IVLE SVN Group Repositories Configuration\n")
112
    f.write("# Auto-generated on %s\n" % time.asctime())
113
    f.write("\n")
1080.1.44 by William Grant
ivle.makeuser: Port rebuild_svn_group_config() to Storm.
114
    for group in store.find(ProjectGroup):
115
        offering = group.project_set.offering
116
        reponame = "_".join([offering.subject.short_name,
117
                             offering.semester.year,
118
                             offering.semester.semester,
119
                             group.name])
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
120
        f.write("[%s:/]\n"%reponame)
1080.1.44 by William Grant
ivle.makeuser: Port rebuild_svn_group_config() to Storm.
121
        for user in group.members:
122
            f.write("%s = rw\n" % user.login)
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
123
        f.write("\n")
124
    f.close()
1229 by Matt Giuca
ivle.makeuser: rebuild_svn_config and rebuild_svn_group_config now require a
125
    os.rename(temp_name, conf_name)
126
    chown_to_webserver(conf_name)
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
127
1230 by Matt Giuca
ivle.makeuser: make_svn_auth now requires a config argument.
128
def make_svn_auth(store, login, config, throw_on_error=True):
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
129
    """Setup svn authentication for the given user.
1080.1.7 by matt.giuca
The new ivle.database.User class is now used in Request and usrmgt, which
130
       Uses the given DB store object. Does not commit to the db.
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
131
    """
1230 by Matt Giuca
ivle.makeuser: make_svn_auth now requires a config argument.
132
    # filename is, eg, /var/lib/ivle/svn/ivle.auth
133
    filename = config['paths']['svn']['auth_ivle']
1197 by Matt Giuca
ivle.chat, ivle.database, ivle.makeuser: Replaced use of md5 library with
134
    passwd = hashlib.md5(uuid.uuid4().bytes).hexdigest()
1230 by Matt Giuca
ivle.makeuser: make_svn_auth now requires a config argument.
135
    if os.path.exists(filename):
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
136
        create = ""
137
    else:
138
        create = "c"
139
1080.1.7 by matt.giuca
The new ivle.database.User class is now used in Request and usrmgt, which
140
    user = ivle.database.User.get_by_login(store, login)
141
    user.svn_pass = unicode(passwd)
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
142
1230 by Matt Giuca
ivle.makeuser: make_svn_auth now requires a config argument.
143
    res = os.system("htpasswd -%smb %s %s %s" % (create, filename,
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
144
                                              login, passwd))
145
    if res != 0 and throw_on_error:
146
        raise Exception("Unable to create ivle-auth for %s" % login)
147
148
    # Make sure the file is owned by the web server
149
    if create == "c":
1230 by Matt Giuca
ivle.makeuser: make_svn_auth now requires a config argument.
150
        chown_to_webserver(filename)
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
151
152
    return passwd
153
154
def generate_manifest(basedir, targetdir, parent=''):
155
    """ From a basedir and a targetdir work out which files are missing or out 
156
    of date and need to be added/updated and which files are redundant and need 
157
    to be removed.
158
    
159
    parent: This is used for the recursive call to track the relative paths 
160
    that we have decended.
161
    """
162
    
163
    cmp = filecmp.dircmp(basedir, targetdir)
164
165
    # Add all new files and files that have changed
166
    to_add = [os.path.join(parent,x) for x in (cmp.left_only + cmp.diff_files)]
167
168
    # Remove files that are redundant
169
    to_remove = [os.path.join(parent,x) for x in cmp.right_only]
170
    
171
    # Recurse
172
    for d in cmp.common_dirs:
173
        newbasedir = os.path.join(basedir, d)
174
        newtargetdir = os.path.join(targetdir, d)
175
        newparent = os.path.join(parent, d)
176
        (sadd,sremove) = generate_manifest(newbasedir, newtargetdir, newparent)
177
        to_add += sadd
178
        to_remove += sremove
179
180
    return (to_add, to_remove)
181
182
1231 by Matt Giuca
ivle.makeuser: make_jail now requires a config argument.
183
def make_jail(user, config, force=True):
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
184
    """Creates a new user's jail space, in the jail directory as configured in
185
    conf.py.
186
187
    This only creates things within /home - everything else is expected to be
188
    part of another UnionFS branch.
189
190
    Returns the path to the user's home directory.
191
192
    Chowns the user's directory within the jail to the given UID.
193
194
    force: If false, exception if jail already exists for this user.
195
    If true (default), overwrites it, but preserves home directory.
196
    """
197
    # MUST run as root or some of this may fail
198
    if os.getuid() != 0:
199
        raise Exception("Must run make_jail as root")
200
    
201
    # tempdir is for putting backup homes in
1231 by Matt Giuca
ivle.makeuser: make_jail now requires a config argument.
202
    jail_src_base = config['paths']['jails']['src']
203
    tempdir = os.path.join(jail_src_base, '__temp__')
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
204
    if not os.path.exists(tempdir):
205
        os.makedirs(tempdir)
206
    elif not os.path.isdir(tempdir):
207
        os.unlink(tempdir)
208
        os.mkdir(tempdir)
1231 by Matt Giuca
ivle.makeuser: make_jail now requires a config argument.
209
    userdir = os.path.join(jail_src_base, user.login)
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
210
    homedir = os.path.join(userdir, 'home')
1080.1.19 by me at id
ivle.makeuser.make_jail: Just take an ivle.database.User, rather than some
211
    userhomedir = os.path.join(homedir, user.login)   # Return value
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
212
213
    if os.path.exists(userdir):
214
        if not force:
215
            raise Exception("User's jail already exists")
216
        # User jail already exists. Blow it away but preserve their home
217
        # directory. It should be all that is there anyway, but you never
218
        # know!
219
        # Ignore warnings about the use of tmpnam
220
        warnings.simplefilter('ignore')
221
        homebackup = os.tempnam(tempdir)
222
        warnings.resetwarnings()
1186 by Matt Giuca
ivle.makeuser: Fixed odd code which would create the home directory, then
223
        # Back up the /home directory, delete the entire jail, recreate the
224
        # jail directory tree, then copy the /home back
225
        # NOTE that shutil.move changed in Python 2.6, it now moves a
226
        # directory INTO the target (like `mv`), which it didn't use to do.
227
        # This code works regardless.
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
228
        shutil.move(homedir, homebackup)
229
        shutil.rmtree(userdir)
1186 by Matt Giuca
ivle.makeuser: Fixed odd code which would create the home directory, then
230
        os.makedirs(userdir)
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
231
        shutil.move(homebackup, homedir)
232
        # Change the ownership of all the files to the right unixid
233
        logging.debug("chown %s's home directory files to uid %d"
1080.1.19 by me at id
ivle.makeuser.make_jail: Just take an ivle.database.User, rather than some
234
            %(user.login, user.unixid))
1114 by William Grant
ivle.makeuser.make_jail() no longer uses os.walk() to recursively set
235
        os.spawnvp(os.P_WAIT, 'chown', ['chown', '-R', '%d:%d' % (user.unixid,
236
                                        user.unixid), userhomedir])
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
237
    else:
238
        # No user jail exists
239
        # Set up the user's home directory
240
        os.makedirs(userhomedir)
241
        # Chown (and set the GID to the same as the UID).
1080.1.19 by me at id
ivle.makeuser.make_jail: Just take an ivle.database.User, rather than some
242
        os.chown(userhomedir, user.unixid, user.unixid)
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
243
        # Chmod to rwxr-xr-x (755)
244
        os.chmod(userhomedir, 0755)
245
1232 by Matt Giuca
ivle.makeuser: make_ivle_conf now requires a config argument.
246
    make_ivle_conf(user.login, userdir, user.svn_pass, config)
1231 by Matt Giuca
ivle.makeuser: make_jail now requires a config argument.
247
    make_etc_passwd(user.login, userdir, config['paths']['jails']['template'],
248
                    user.unixid)
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
249
250
    return userhomedir
251
1232 by Matt Giuca
ivle.makeuser: make_ivle_conf now requires a config argument.
252
def make_ivle_conf(username, user_jail_dir, svn_pass, sys_config):
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
253
    """
254
    Creates (overwriting any existing file, and creating directories) a
1187 by Matt Giuca
Stopped clobbering conf.py within the jail, using a proper ivle.conf instead.
255
    file /etc/ivle/ivle.conf in a given user's jail.
1232 by Matt Giuca
ivle.makeuser: make_ivle_conf now requires a config argument.
256
    @param username: Username.
1236 by Matt Giuca
Fixed docstring in makeuser (don't refer to ivle.conf).
257
    @param user_jail_dir: User's jail dir, ie. ['jails']['src'] + username
1232 by Matt Giuca
ivle.makeuser: make_ivle_conf now requires a config argument.
258
    @param svn_pass: User's SVN password.
259
    @param sys_config: An ivle.config.Config object (the system-wide config).
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
260
    """
1187 by Matt Giuca
Stopped clobbering conf.py within the jail, using a proper ivle.conf instead.
261
    conf_path = os.path.join(user_jail_dir, "etc/ivle/ivle.conf")
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
262
    os.makedirs(os.path.dirname(conf_path))
263
1092.1.1 by William Grant
[Uber-commit of holiday work because I lacked a local copy of the branch.]
264
    # In the "in-jail" version of conf, we don't need MOST of the details
265
    # (it would be a security risk to have them here).
1187 by Matt Giuca
Stopped clobbering conf.py within the jail, using a proper ivle.conf instead.
266
    # So we just write root_dir.
267
    conf_obj = ivle.config.Config(blank=True)
268
    conf_obj.filename = conf_path
1232 by Matt Giuca
ivle.makeuser: make_ivle_conf now requires a config argument.
269
    conf_obj['urls']['root'] = sys_config['urls']['root']
270
    conf_obj['urls']['public_host'] = sys_config['urls']['public_host']
271
    conf_obj['urls']['svn_addr'] = sys_config['urls']['svn_addr']
1187 by Matt Giuca
Stopped clobbering conf.py within the jail, using a proper ivle.conf instead.
272
    conf_obj['user_info']['login'] = username
273
    conf_obj['user_info']['svn_pass'] = svn_pass
274
    conf_obj.write()
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
275
276
    # Make this file world-readable
277
    # (chmod 644 conf_path)
278
    os.chmod(conf_path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP
279
                        | stat.S_IROTH)
280
281
def make_etc_passwd(username, user_jail_dir, template_dir, unixid):
282
    """
283
    Creates /etc/passwd in the given user's jail. This will be identical to
284
    that in the template jail, except for the added entry for this user.
285
    """
286
    template_passwd_path = os.path.join(template_dir, "etc/passwd")
287
    passwd_path = os.path.join(user_jail_dir, "etc/passwd")
288
    passwd_dir = os.path.dirname(passwd_path)
289
    if not os.path.exists(passwd_dir):
290
        os.makedirs(passwd_dir)
291
    shutil.copy(template_passwd_path, passwd_path)
292
    passwd_file = open(passwd_path, 'a')
293
    passwd_file.write('%s:x:%d:%d::/home/%s:/bin/bash'
294
                      % (username, unixid, unixid, username))
295
    passwd_file.close()
296
1233 by Matt Giuca
ivle.makeuser: mount_jail now requires a config argument
297
def mount_jail(login, config):
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
298
    # This is where we'll mount to...
1233 by Matt Giuca
ivle.makeuser: mount_jail now requires a config argument
299
    destdir = os.path.join(config['paths']['jails']['mounts'], login)
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
300
    # ... and this is where we'll get the user bits.
1233 by Matt Giuca
ivle.makeuser: mount_jail now requires a config argument
301
    srcdir = os.path.join(config['paths']['jails']['src'], login)
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
302
    try:
303
        if not os.path.exists(destdir):
304
            os.mkdir(destdir)
305
        if os.system('/bin/mount -t aufs -o dirs=%s:%s=ro none %s'
1233 by Matt Giuca
ivle.makeuser: mount_jail now requires a config argument
306
                     % (srcdir, config['paths']['jails']['template'],
307
                        destdir)) == 0:
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
308
            logging.info("mounted user %s's jail." % login)
309
        else:
310
            logging.error("failed to mount user %s's jail!" % login)
311
    except Exception, message:
312
        logging.warning(str(message))