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).
54
73
def rebuild_svn_config(store, config):
55
74
"""Build the complete SVN configuration file.
57
75
@param config: An ivle.config.Config object.
59
users = store.find(User)
77
users = store.find(ivle.database.User)
79
# TODO: Populate groups with per-offering tutors/lecturers/etc.
60
80
conf_name = config['paths']['svn']['conf']
61
81
temp_name = conf_name + ".new"
62
82
f = open(temp_name, "w")
64
# IVLE SVN repository authorisation configuration
66
""" % {'time': time.asctime()})
83
f.write("# IVLE SVN Repositories Configuration\n")
84
f.write("# Auto-generated on %s\n" % time.asctime())
87
for (g,ls) in groups.iteritems():
88
f.write("%s = %s\n" % (g, ",".join(ls)))
72
""" % {'login': u.login})
91
f.write("[%s:/]\n" % u.login)
92
f.write("%s = rw\n" % u.login)
93
#f.write("@tutor = r\n")
94
#f.write("@lecturer = rw\n")
95
#f.write("@admin = rw\n")
75
98
os.rename(temp_name, conf_name)
76
99
chown_to_webserver(conf_name)
78
101
def rebuild_svn_group_config(store, config):
79
102
"""Build the complete SVN configuration file for groups
81
103
@param config: An ivle.config.Config object.
83
105
conf_name = config['paths']['svn']['group_conf']
84
106
temp_name = conf_name + ".new"
85
107
f = open(temp_name, "w")
88
# IVLE SVN group repository authorisation configuration
91
""" % {'time': time.asctime()})
108
f.write("# IVLE SVN Group Repositories Configuration\n")
109
f.write("# Auto-generated on %s\n" % time.asctime())
93
111
for group in store.find(ProjectGroup):
94
112
offering = group.project_set.offering
95
113
reponame = "_".join([offering.subject.short_name,
96
114
offering.semester.year,
97
115
offering.semester.semester,
100
f.write("[%s:/]\n" % reponame)
117
f.write("[%s:/]\n"%reponame)
101
118
for user in group.members:
102
119
f.write("%s = rw\n" % user.login)
106
122
os.rename(temp_name, conf_name)
107
123
chown_to_webserver(conf_name)
109
125
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.
126
"""Setup svn authentication for the given user.
127
Uses the given DB store object. Does not commit to the db.
115
129
# filename is, eg, /var/lib/ivle/svn/ivle.auth
116
130
filename = config['paths']['svn']['auth_ivle']
123
user = User.get_by_login(store, login)
137
user = ivle.database.User.get_by_login(store, login)
124
138
user.svn_pass = unicode(passwd)
126
res = subprocess.call(['htpasswd', '-%smb' % create,
127
filename, login, passwd])
140
res = os.system("htpasswd -%smb %s %s %s" % (create, filename,
128
142
if res != 0 and throw_on_error:
129
143
raise Exception("Unable to create ivle-auth for %s" % login)
151
def generate_manifest(basedir, targetdir, parent=''):
152
""" From a basedir and a targetdir work out which files are missing or out
153
of date and need to be added/updated and which files are redundant and need
156
parent: This is used for the recursive call to track the relative paths
157
that we have decended.
160
cmp = filecmp.dircmp(basedir, targetdir)
162
# Add all new files and files that have changed
163
to_add = [os.path.join(parent,x) for x in (cmp.left_only + cmp.diff_files)]
165
# Remove files that are redundant
166
to_remove = [os.path.join(parent,x) for x in cmp.right_only]
169
for d in cmp.common_dirs:
170
newbasedir = os.path.join(basedir, d)
171
newtargetdir = os.path.join(targetdir, d)
172
newparent = os.path.join(parent, d)
173
(sadd,sremove) = generate_manifest(newbasedir, newtargetdir, newparent)
177
return (to_add, to_remove)
137
180
def make_jail(user, config, force=True):
138
"""Create or update a user's jail.
181
"""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.
184
This only creates things within /home - everything else is expected to be
185
part of another UnionFS branch.
143
187
Returns the path to the user's home directory.
145
189
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.
191
force: If false, exception if jail already exists for this user.
192
If true (default), overwrites it, but preserves home directory.
150
194
# MUST run as root or some of this may fail
151
195
if os.getuid() != 0:
203
247
return userhomedir
205
249
def make_ivle_conf(username, user_jail_dir, svn_pass, sys_config):
206
"""Generate an ivle.conf for a user's jail.
208
251
Creates (overwriting any existing file, and creating directories) a
209
252
file /etc/ivle/ivle.conf in a given user's jail.
211
253
@param username: Username.
212
254
@param user_jail_dir: User's jail dir, ie. ['jails']['src'] + username
213
255
@param svn_pass: User's SVN password.
249
290
passwd_file.write('%s:x:%d:%d::/home/%s:/bin/bash'
250
291
% (username, unixid, unixid, username))
251
292
passwd_file.close()
294
def mount_jail(login, config):
295
# This is where we'll mount to...
296
destdir = os.path.join(config['paths']['jails']['mounts'], login)
297
# ... and this is where we'll get the user bits.
298
srcdir = os.path.join(config['paths']['jails']['src'], login)
300
if not os.path.exists(destdir):
302
if os.system('/bin/mount -t aufs -o dirs=%s:%s=ro none %s'
303
% (srcdir, config['paths']['jails']['template'],
305
logging.info("mounted user %s's jail." % login)
307
logging.error("failed to mount user %s's jail!" % login)
308
except Exception, message:
309
logging.warning(str(message))