15
15
# along with this program; if not, write to the Free Software
16
16
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
"""User and group filesystem management helpers."""
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
29
# TODO: Sanitize login name and other fields.
30
# Users must not be called "temp" or "template".
32
# TODO: When creating a new home directory, chown it to its owner
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).
48
import ivle.pulldown_subj
31
from ivle.database import ProjectGroup, User
50
from ivle.database import ProjectGroup
33
52
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.
54
Chowns a file so the web server user owns it.
55
(This is useful in setting up Subversion conf files).
39
subprocess.call(['chown', '-R', 'www-data:www-data', filename])
59
os.system("chown -R www-data:www-data %s" % filename)
41
63
def make_svn_repo(path, throw_on_error=True):
42
"""Create a Subversion repository at the given path."""
64
"""Create a Subversion repository at the given path.
44
res = subprocess.call(['svnadmin', 'create', path])
67
res = os.system("svnadmin create '%s'" % path)
45
68
if res != 0 and throw_on_error:
46
69
raise Exception("Cannot create repository: %s" % path)
47
70
except Exception, exc:
54
77
def rebuild_svn_config(store, config):
55
78
"""Build the complete SVN configuration file.
57
79
@param config: An ivle.config.Config object.
59
users = store.find(User)
81
users = store.find(ivle.database.User)
83
# TODO: Populate groups with per-offering tutors/lecturers/etc.
60
84
conf_name = config['paths']['svn']['conf']
61
85
temp_name = conf_name + ".new"
62
86
f = open(temp_name, "w")
64
# IVLE SVN repository authorisation configuration
66
""" % {'time': time.asctime()})
87
f.write("# IVLE SVN Repositories Configuration\n")
88
f.write("# Auto-generated on %s\n" % time.asctime())
91
for (g,ls) in groups.iteritems():
92
f.write("%s = %s\n" % (g, ",".join(ls)))
72
""" % {'login': u.login})
95
f.write("[%s:/]\n" % u.login)
96
f.write("%s = rw\n" % u.login)
97
#f.write("@tutor = r\n")
98
#f.write("@lecturer = rw\n")
99
#f.write("@admin = rw\n")
75
102
os.rename(temp_name, conf_name)
76
103
chown_to_webserver(conf_name)
78
105
def rebuild_svn_group_config(store, config):
79
106
"""Build the complete SVN configuration file for groups
81
107
@param config: An ivle.config.Config object.
83
109
conf_name = config['paths']['svn']['group_conf']
84
110
temp_name = conf_name + ".new"
85
111
f = open(temp_name, "w")
88
# IVLE SVN group repository authorisation configuration
91
""" % {'time': time.asctime()})
112
f.write("# IVLE SVN Group Repositories Configuration\n")
113
f.write("# Auto-generated on %s\n" % time.asctime())
93
115
for group in store.find(ProjectGroup):
94
116
offering = group.project_set.offering
95
117
reponame = "_".join([offering.subject.short_name,
96
118
offering.semester.year,
97
119
offering.semester.semester,
100
f.write("[%s:/]\n" % reponame)
121
f.write("[%s:/]\n"%reponame)
101
122
for user in group.members:
102
123
f.write("%s = rw\n" % user.login)
106
126
os.rename(temp_name, conf_name)
107
127
chown_to_webserver(conf_name)
109
129
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.
130
"""Setup svn authentication for the given user.
131
Uses the given DB store object. Does not commit to the db.
115
133
# filename is, eg, /var/lib/ivle/svn/ivle.auth
116
134
filename = config['paths']['svn']['auth_ivle']
123
user = User.get_by_login(store, login)
141
user = ivle.database.User.get_by_login(store, login)
124
142
user.svn_pass = unicode(passwd)
126
res = subprocess.call(['htpasswd', '-%smb' % create,
127
filename, login, passwd])
144
res = os.system("htpasswd -%smb %s %s %s" % (create, filename,
128
146
if res != 0 and throw_on_error:
129
147
raise Exception("Unable to create ivle-auth for %s" % login)
155
def generate_manifest(basedir, targetdir, parent=''):
156
""" From a basedir and a targetdir work out which files are missing or out
157
of date and need to be added/updated and which files are redundant and need
160
parent: This is used for the recursive call to track the relative paths
161
that we have decended.
164
cmp = filecmp.dircmp(basedir, targetdir)
166
# Add all new files and files that have changed
167
to_add = [os.path.join(parent,x) for x in (cmp.left_only + cmp.diff_files)]
169
# Remove files that are redundant
170
to_remove = [os.path.join(parent,x) for x in cmp.right_only]
173
for d in cmp.common_dirs:
174
newbasedir = os.path.join(basedir, d)
175
newtargetdir = os.path.join(targetdir, d)
176
newparent = os.path.join(parent, d)
177
(sadd,sremove) = generate_manifest(newbasedir, newtargetdir, newparent)
181
return (to_add, to_remove)
137
184
def make_jail(user, config, force=True):
138
"""Create or update a user's jail.
185
"""Creates a new user's jail space, in the jail directory as configured in
140
Only the user-specific parts of the jail are created here - everything
141
else is expected to be part of another aufs branch.
188
This only creates things within /home - everything else is expected to be
189
part of another UnionFS branch.
143
191
Returns the path to the user's home directory.
145
193
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.
195
force: If false, exception if jail already exists for this user.
196
If true (default), overwrites it, but preserves home directory.
150
198
# MUST run as root or some of this may fail
151
199
if os.getuid() != 0:
203
251
return userhomedir
205
253
def make_ivle_conf(username, user_jail_dir, svn_pass, sys_config):
206
"""Generate an ivle.conf for a user's jail.
208
255
Creates (overwriting any existing file, and creating directories) a
209
256
file /etc/ivle/ivle.conf in a given user's jail.
211
257
@param username: Username.
212
@param user_jail_dir: User's jail dir, ie. ['jails']['src'] + username
258
@param user_jail_dir: User's jail dir, ie. ivle.conf.jail_base + username
213
259
@param svn_pass: User's SVN password.
214
260
@param sys_config: An ivle.config.Config object (the system-wide config).
249
294
passwd_file.write('%s:x:%d:%d::/home/%s:/bin/bash'
250
295
% (username, unixid, unixid, username))
251
296
passwd_file.close()
298
def mount_jail(login, config):
299
# This is where we'll mount to...
300
destdir = os.path.join(config['paths']['jails']['mounts'], login)
301
# ... and this is where we'll get the user bits.
302
srcdir = os.path.join(config['paths']['jails']['src'], login)
304
if not os.path.exists(destdir):
306
if os.system('/bin/mount -t aufs -o dirs=%s:%s=ro none %s'
307
% (srcdir, config['paths']['jails']['template'],
309
logging.info("mounted user %s's jail." % login)
311
logging.error("failed to mount user %s's jail!" % login)
312
except Exception, message:
313
logging.warning(str(message))