1
# IVLE - Informatics Virtual Learning Environment
2
# Copyright (C) 2007-2009 The University of Melbourne
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.
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.
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
18
"""User and group filesystem management helpers."""
31
from ivle.database import ProjectGroup, User
33
def chown_to_webserver(filename):
34
"""chown a directory and its contents to the web server.
36
Recursively chowns a file or directory so the web server user owns it.
39
subprocess.call(['chown', '-R', 'www-data:www-data', filename])
41
def make_svn_repo(path, throw_on_error=True):
42
"""Create a Subversion repository at the given path."""
44
res = subprocess.call(['svnadmin', 'create', path])
45
if res != 0 and throw_on_error:
46
raise Exception("Cannot create repository: %s" % path)
47
except Exception, exc:
52
chown_to_webserver(path)
54
def rebuild_svn_config(store, config):
55
"""Build the complete SVN configuration file.
57
@param config: An ivle.config.Config object.
59
users = store.find(User)
60
conf_name = config['paths']['svn']['conf']
61
temp_name = conf_name + ".new"
62
f = open(temp_name, "w")
64
# IVLE SVN repository authorisation configuration
66
""" % {'time': time.asctime()})
72
""" % {'login': u.login})
75
os.rename(temp_name, conf_name)
76
chown_to_webserver(conf_name)
78
def rebuild_svn_group_config(store, config):
79
"""Build the complete SVN configuration file for groups
81
@param config: An ivle.config.Config object.
83
conf_name = config['paths']['svn']['group_conf']
84
temp_name = conf_name + ".new"
85
f = open(temp_name, "w")
88
# IVLE SVN group repository authorisation configuration
91
""" % {'time': time.asctime()})
93
for group in store.find(ProjectGroup):
94
offering = group.project_set.offering
95
reponame = "_".join([offering.subject.short_name,
96
offering.semester.year,
97
offering.semester.semester,
100
f.write("[%s:/]\n" % reponame)
101
for user in group.members:
102
f.write("%s = rw\n" % user.login)
106
os.rename(temp_name, conf_name)
107
chown_to_webserver(conf_name)
109
def make_svn_auth(store, login, config, throw_on_error=True):
110
"""Create a Subversion password for a user.
112
Generates a new random Subversion password, and assigns it to the user.
113
The password is added to Apache's Subversion authentication file.
115
# filename is, eg, /var/lib/ivle/svn/ivle.auth
116
filename = config['paths']['svn']['auth_ivle']
117
passwd = hashlib.md5(uuid.uuid4().bytes).hexdigest()
118
if os.path.exists(filename):
123
user = User.get_by_login(store, login)
124
user.svn_pass = unicode(passwd)
126
res = subprocess.call(['htpasswd', '-%smb' % create,
127
filename, login, passwd])
128
if res != 0 and throw_on_error:
129
raise Exception("Unable to create ivle-auth for %s" % login)
131
# Make sure the file is owned by the web server
133
chown_to_webserver(filename)
137
def make_jail(user, config, force=True):
138
"""Create or update a user's jail.
140
Only the user-specific parts of the jail are created here - everything
141
else is expected to be part of another aufs branch.
143
Returns the path to the user's home directory.
145
Chowns the user's directory within the jail to the given UID.
147
@param force: If False, raise an exception if the user already has a jail.
148
If True (default), rebuild the jail preserving /home.
150
# MUST run as root or some of this may fail
152
raise Exception("Must run make_jail as root")
154
# tempdir is for putting backup homes in
155
jail_src_base = config['paths']['jails']['src']
156
tempdir = os.path.join(jail_src_base, '__temp__')
157
if not os.path.exists(tempdir):
159
elif not os.path.isdir(tempdir):
162
userdir = os.path.join(jail_src_base, user.login)
163
homedir = os.path.join(userdir, 'home')
164
userhomedir = os.path.join(homedir, user.login) # Return value
166
if os.path.exists(userdir):
168
raise Exception("User's jail already exists")
169
# User jail already exists. Blow it away but preserve their home
170
# directory. It should be all that is there anyway, but you never
172
# Ignore warnings about the use of tmpnam
173
warnings.simplefilter('ignore')
174
homebackup = os.tempnam(tempdir)
175
warnings.resetwarnings()
176
# Back up the /home directory, delete the entire jail, recreate the
177
# jail directory tree, then copy the /home back
178
# NOTE that shutil.move changed in Python 2.6, it now moves a
179
# directory INTO the target (like `mv`), which it didn't use to do.
180
# This code works regardless.
181
shutil.move(homedir, homebackup)
182
shutil.rmtree(userdir)
184
shutil.move(homebackup, homedir)
185
# Change the ownership of all the files to the right unixid
186
logging.debug("chown %s's home directory files to uid %d"
187
%(user.login, user.unixid))
188
os.spawnvp(os.P_WAIT, 'chown', ['chown', '-R', '%d:%d' % (user.unixid,
189
user.unixid), userhomedir])
191
# No user jail exists
192
# Set up the user's home directory
193
os.makedirs(userhomedir)
194
# Chown (and set the GID to the same as the UID).
195
os.chown(userhomedir, user.unixid, user.unixid)
196
# Chmod to rwxr-xr-x (755)
197
os.chmod(userhomedir, 0755)
199
make_ivle_conf(user.login, userdir, user.svn_pass, config)
200
make_etc_passwd(user.login, userdir, config['paths']['jails']['template'],
205
def make_ivle_conf(username, user_jail_dir, svn_pass, sys_config):
206
"""Generate an ivle.conf for a user's jail.
208
Creates (overwriting any existing file, and creating directories) a
209
file /etc/ivle/ivle.conf in a given user's jail.
211
@param username: Username.
212
@param user_jail_dir: User's jail dir, ie. ['jails']['src'] + username
213
@param svn_pass: User's SVN password.
214
@param sys_config: An ivle.config.Config object (the system-wide config).
216
conf_path = os.path.join(user_jail_dir, "etc/ivle/ivle.conf")
217
os.makedirs(os.path.dirname(conf_path))
219
# In the "in-jail" version of conf, we don't need MOST of the details
220
# (it would be a security risk to have them here).
221
# So we just write root_dir.
222
conf_obj = ivle.config.Config(blank=True)
223
conf_obj.filename = conf_path
224
conf_obj['urls']['root'] = sys_config['urls']['root']
225
conf_obj['urls']['public_host'] = sys_config['urls']['public_host']
226
conf_obj['urls']['svn_addr'] = sys_config['urls']['svn_addr']
227
conf_obj['user_info']['login'] = username
228
conf_obj['user_info']['svn_pass'] = svn_pass
231
# Make this file world-readable
232
# (chmod 644 conf_path)
233
os.chmod(conf_path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP
236
def make_etc_passwd(username, user_jail_dir, template_dir, unixid):
237
"""Create a passwd file for a user's jail.
239
Creates /etc/passwd in the given user's jail. This will be identical to
240
that in the template jail, except for the added entry for this user.
242
template_passwd_path = os.path.join(template_dir, "etc/passwd")
243
passwd_path = os.path.join(user_jail_dir, "etc/passwd")
244
passwd_dir = os.path.dirname(passwd_path)
245
if not os.path.exists(passwd_dir):
246
os.makedirs(passwd_dir)
247
shutil.copy(template_passwd_path, passwd_path)
248
passwd_file = open(passwd_path, 'a')
249
passwd_file.write('%s:x:%d:%d::/home/%s:/bin/bash'
250
% (username, unixid, unixid, username))