30
from storm.expr import Select, Max
31
from ivle.database import ProjectGroup, User
33
from ivle.database import (User, ProjectGroup, Assessed, ProjectSubmission,
34
Project, ProjectSet, Offering, Enrolment, Subject, Semester)
33
36
def chown_to_webserver(filename):
34
37
"""chown a directory and its contents to the web server.
72
""" % {'login': u.login})
75
""" % {'login': u.login.encode('utf-8')})
77
# Now we need to grant offering tutors and lecturers access to the latest
78
# submissions in their offerings. There are much prettier ways to do this,
79
# but a lot of browser requests call this function, so it needs to be
80
# fast. We can grab all of the paths needing authorisation directives with
81
# a single query, and we cache the list of viewers for each offering.
82
offering_viewers_cache = {}
83
for (login, psid, pspath, offeringid) in store.find(
84
(User.login, ProjectSubmission.id, ProjectSubmission.path,
86
Assessed.id == ProjectSubmission.assessed_id,
87
User.id == Assessed.user_id,
88
Project.id == Assessed.project_id,
89
ProjectSet.id == Project.project_set_id,
90
Offering.id == ProjectSet.offering_id,
91
ProjectSubmission.date_submitted == Select(
92
Max(ProjectSubmission.date_submitted),
93
ProjectSubmission.assessed_id == Assessed.id,
94
tables=ProjectSubmission
98
# Do we already have the list of logins authorised for this offering
99
# cached? If not, get it.
100
if offeringid not in offering_viewers_cache:
101
offering_viewers_cache[offeringid] = list(store.find(
103
User.id == Enrolment.user_id,
104
Enrolment.offering_id == offeringid,
105
Enrolment.role.is_in((u'tutor', u'lecturer')),
106
Enrolment.active == True,
113
""" % {'login': login.encode('utf-8'), 'id': psid,
114
'path': pspath.encode('utf-8')})
116
for viewer_login in offering_viewers_cache[offeringid]:
117
# We don't want to override the owner's write privilege,
118
# so we don't add them to the read-only ACL.
119
if login != viewer_login:
120
f.write("%s = r\n" % viewer_login.encode('utf-8'))
75
123
os.rename(temp_name, conf_name)
91
139
""" % {'time': time.asctime()})
141
group_members_cache = {}
93
142
for group in store.find(ProjectGroup):
94
143
offering = group.project_set.offering
95
144
reponame = "_".join([offering.subject.short_name,
96
145
offering.semester.year,
97
offering.semester.semester,
146
offering.semester.url_name,
100
f.write("[%s:/]\n" % reponame)
149
f.write("[%s:/]\n" % reponame.encode('utf-8'))
150
if group.id not in group_members_cache:
151
group_members_cache[group.id] = set()
101
152
for user in group.members:
102
f.write("%s = rw\n" % user.login)
153
group_members_cache[group.id].add(user.login)
154
f.write("%s = rw\n" % user.login.encode('utf-8'))
157
# Now we need to grant offering tutors and lecturers access to the latest
158
# submissions in their offerings. There are much prettier ways to do this,
159
# but a lot of browser requests call this function, so it needs to be
160
# fast. We can grab all of the paths needing authorisation directives with
161
# a single query, and we cache the list of viewers for each offering.
162
offering_viewers_cache = {}
163
for (ssn, year, sem, name, psid, pspath, gid, offeringid) in store.find(
164
(Subject.short_name, Semester.year, Semester.url_name,
165
ProjectGroup.name, ProjectSubmission.id, ProjectSubmission.path,
166
ProjectGroup.id, Offering.id),
167
Assessed.id == ProjectSubmission.assessed_id,
168
ProjectGroup.id == Assessed.project_group_id,
169
Project.id == Assessed.project_id,
170
ProjectSet.id == Project.project_set_id,
171
Offering.id == ProjectSet.offering_id,
172
Subject.id == Offering.subject_id,
173
Semester.id == Offering.semester_id,
174
ProjectSubmission.date_submitted == Select(
175
Max(ProjectSubmission.date_submitted),
176
ProjectSubmission.assessed_id == Assessed.id,
177
tables=ProjectSubmission
181
reponame = "_".join([ssn, year, sem, name])
183
# Do we already have the list of logins authorised for this offering
184
# cached? If not, get it.
185
if offeringid not in offering_viewers_cache:
186
offering_viewers_cache[offeringid] = list(store.find(
188
User.id == Enrolment.user_id,
189
Enrolment.offering_id == offeringid,
190
Enrolment.role.is_in((u'tutor', u'lecturer')),
191
Enrolment.active == True,
198
""" % {'repo': reponame.encode('utf-8'), 'id': psid,
199
'path': pspath.encode('utf-8')})
201
for viewer_login in offering_viewers_cache[offeringid]:
202
# Skip existing group members, or they can't write to it any more.
203
if viewer_login not in group_members_cache[gid]:
204
f.write("%s = r\n" % viewer_login)
106
207
os.rename(temp_name, conf_name)
107
208
chown_to_webserver(conf_name)
115
216
# filename is, eg, /var/lib/ivle/svn/ivle.auth
116
217
filename = config['paths']['svn']['auth_ivle']
117
passwd = hashlib.md5(uuid.uuid4().bytes).hexdigest()
118
218
if os.path.exists(filename):
123
223
user = User.get_by_login(store, login)
124
user.svn_pass = unicode(passwd)
225
if user.svn_pass is None:
226
passwd = hashlib.md5(uuid.uuid4().bytes).hexdigest()
227
user.svn_pass = unicode(passwd)
126
229
res = subprocess.call(['htpasswd', '-%smb' % create,
127
filename, login, passwd])
230
filename, login, user.svn_pass])
128
231
if res != 0 and throw_on_error:
129
232
raise Exception("Unable to create ivle-auth for %s" % login)
178
282
# NOTE that shutil.move changed in Python 2.6, it now moves a
179
283
# directory INTO the target (like `mv`), which it didn't use to do.
180
284
# This code works regardless.
181
shutil.move(homedir, homebackup)
285
shutil.move(userhomedir, homebackup)
182
286
shutil.rmtree(userdir)
184
shutil.move(homebackup, homedir)
288
shutil.move(homebackup, userhomedir)
185
289
# Change the ownership of all the files to the right unixid
186
290
logging.debug("chown %s's home directory files to uid %d"
187
291
%(user.login, user.unixid))
213
319
@param svn_pass: User's SVN password.
214
320
@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))
322
conf_path = os.path.join(user_jail_dir, "home/.ivle.conf")
323
if not os.path.exists(os.path.dirname(conf_path)):
324
os.makedirs(os.path.dirname(conf_path))
219
326
# In the "in-jail" version of conf, we don't need MOST of the details
220
327
# (it would be a security risk to have them here).
221
328
# So we just write root_dir.
222
329
conf_obj = ivle.config.Config(blank=True)
223
330
conf_obj.filename = conf_path
331
conf_obj['urls'] = {}
224
332
conf_obj['urls']['root'] = sys_config['urls']['root']
225
333
conf_obj['urls']['public_host'] = sys_config['urls']['public_host']
226
334
conf_obj['urls']['svn_addr'] = sys_config['urls']['svn_addr']
335
conf_obj['user_info'] = {}
227
336
conf_obj['user_info']['login'] = username
228
337
conf_obj['user_info']['svn_pass'] = svn_pass
239
348
Creates /etc/passwd in the given user's jail. This will be identical to
240
349
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")
351
template_passwd_path = os.path.join(template_dir, "home/.passwd")
352
passwd_path = os.path.join(user_jail_dir, "home/.passwd")
244
353
passwd_dir = os.path.dirname(passwd_path)
245
354
if not os.path.exists(passwd_dir):
246
355
os.makedirs(passwd_dir)