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

465 by mattgiuca
Added new app: userservice, which is an ajax service for user management
1
# IVLE
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
# App: userservice
19
# Author: Matt Giuca
20
# Date: 14/2/2008
21
478 by mattgiuca
scripts/usrmgr-server: Renamed actions from dashes to underscores.
22
# Provides an Ajax service for handling user management requests.
23
# This includes when a user logs in for the first time.
24
486 by mattgiuca
caps: Added a few new capabilities.
25
### Actions ###
26
27
# The one-and-only path segment to userservice determines the action being
28
# undertaken.
29
# All actions require that you are logged in.
30
# All actions require method = POST, unless otherwise stated.
31
32
# userservice/activate_me
33
# Required cap: None
34
# declaration = "I accept the IVLE Terms of Service"
35
# Activate the currently-logged-in user's account. Requires that "declaration"
36
# is as above, and that the user's state is "no_agreement".
37
38
# TODO
39
# userservice/enable_user
40
# Required cap: CAP_UPDATEUSER
41
# Enable a user whose account has been disabled. Does not work for
42
# no_agreement or pending users.
43
# login = Login name of user to enable.
44
45
# TODO
46
# userservice/disable_user
47
# Required cap: CAP_UPDATEUSER
48
# Disable a user's account. Does not work for no_agreement or pending users.
49
# login = Login name of user to disable.
50
930 by dcoles
Userservice: Added get_enrolments handler to allow a JSON query of a students
51
# userservice/get_enrolments
52
# Required cap: None (for yourself)
53
# Returns a JSON encoded listing of a students is enrollments
54
981 by dcoles
Groups: Added in support for creating groups in the database through
55
# PROJECTS AND GROUPS
56
1001 by dcoles
Groups: Added the view half of the group admin panel. This lets people with the
57
# userservice/get_project_groups
58
# Required cap: None
59
# Returns all the project groups in an offering grouped by project set
60
# Required:
61
#   offeringid
62
981 by dcoles
Groups: Added in support for creating groups in the database through
63
# userservice/create_project_set
64
# Required cap: CAP_MANAGEPROJECTS
984 by dcoles
Groups: Added userservice/create_project call. You can now create a project in
65
# Creates a project set for a offering
981 by dcoles
Groups: Added in support for creating groups in the database through
66
# Required:
67
#   offeringid, max_students_per_group
984 by dcoles
Groups: Added userservice/create_project call. You can now create a project in
68
# Returns:
69
#   projectsetid
981 by dcoles
Groups: Added in support for creating groups in the database through
70
71
# userservice/create_project
72
# Required cap: CAP_MANAGEPROJECTS
73
# Creates a project in a specific project set
74
# Required:
75
#   projectsetid
76
# Optional:
984 by dcoles
Groups: Added userservice/create_project call. You can now create a project in
77
#   synopsis, url, deadline
78
# Returns:
79
#   projectid
981 by dcoles
Groups: Added in support for creating groups in the database through
80
81
# userservice/create_group
82
# Required cap: CAP_MANAGEGROUPS
83
# Creates a project group in a specific project set
84
# Required:
85
#   projectsetid, groupnm
86
# Optional:
87
#   nick
88
1004 by dcoles
Groups: Now you can add people to a group as well as creating groups from the
89
# userservice/get_group_membership
90
# Required cap: None
91
# Returns two lists. One of people in the group and one of people not in the 
92
# group (but enroled in the offering)
93
# Required:
94
#   groupid
95
981 by dcoles
Groups: Added in support for creating groups in the database through
96
# userservice/assign_to_group
97
# Required cap: CAP_MANAGEGROUPS
98
# Assigns a user to a project group
1004 by dcoles
Groups: Now you can add people to a group as well as creating groups from the
99
# Required: login, groupid
981 by dcoles
Groups: Added in support for creating groups in the database through
100
465 by mattgiuca
Added new app: userservice, which is an ajax service for user management
101
import os
102
import sys
1080.1.80 by William Grant
www/apps/userservice: Port create_group to Storm.
103
import datetime
465 by mattgiuca
Added new app: userservice, which is an ajax service for user management
104
105
import cjson
106
1080.1.7 by matt.giuca
The new ivle.database.User class is now used in Request and usrmgt, which
107
import ivle.database
1101 by William Grant
Privileges (apart from admin) are now offering-local, not global.
108
from ivle import (util, chat)
1099.1.161 by William Grant
Move ivle.dispatch.login.get_user_details() to ivle.webapp.security.
109
from ivle.webapp.security import get_user_details
1080.1.73 by William Grant
www/apps/userservice: create_user now creates and enrols the User itself, not
110
import ivle.pulldown_subj
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
111
1606 by William Grant
Restrict privileges on group-related userservice actions to users with admin_groups on the offering.
112
from ivle.rpc.decorators import require_method, require_admin
1080.1.90 by William Grant
ivle.rpc.decorators: Add (new package, too). Has a couple of decorators to
113
1080.1.12 by me at id
ivle.auth.autherror: Remove, moving AuthError into ivle.auth itself.
114
from ivle.auth import AuthError, authenticate
1078 by chadnickbok
Updated the settings page to require the old password
115
import urllib
116
1606.1.4 by William Grant
Display a nice error when trying to add a group with an invalid name.
117
from ivle.webapp.base.forms import VALID_URL_NAME
1099.1.132 by William Grant
Direct port of userservice to the new framework.
118
from ivle.webapp.base.views import BaseView
119
from ivle.webapp.base.plugins import ViewPlugin
1099.1.134 by William Grant
Replace most userservice req.throw_error()s with new exceptions.
120
from ivle.webapp.errors import NotFound, BadRequest, Unauthorized
1294.2.65 by William Grant
Port userservice.
121
from ivle.webapp import ApplicationRoot
1099.1.132 by William Grant
Direct port of userservice to the new framework.
122
465 by mattgiuca
Added new app: userservice, which is an ajax service for user management
123
# The user must send this declaration message to ensure they acknowledge the
124
# TOS
125
USER_DECLARATION = "I accept the IVLE Terms of Service"
126
1099.1.132 by William Grant
Direct port of userservice to the new framework.
127
class UserServiceView(BaseView):
1294.2.65 by William Grant
Port userservice.
128
    subpath_allowed = True
1099.1.132 by William Grant
Direct port of userservice to the new framework.
129
130
    def authorize(self, req):
1099.1.127 by William Grant
Implement ToS acceptance in the new login machinery. Now implemented through
131
        # XXX: activate_me isn't called by a valid user, so is special for now.
1099.1.132 by William Grant
Direct port of userservice to the new framework.
132
        if req.path == 'activate_me' and get_user_details(req) is not None:
133
            return True
134
        return req.user is not None
135
136
    def render(self, req):
137
        # The path determines which "command" we are receiving
138
        fields = req.get_fieldstorage()
139
        try:
140
            func = actions_map[self.path]
141
        except KeyError:
142
            raise NotFound()
1606.1.5 by William Grant
Call the field X-IVLE-Error instead (to avoid potential conflicts with icky fileservice), and populate it in the root XHTMLErrorView.
143
        func(req, fields)
1099.1.132 by William Grant
Direct port of userservice to the new framework.
144
1294.2.65 by William Grant
Port userservice.
145
    @property
146
    def path(self):
147
        return os.path.join(*self.subpath) if self.subpath else ''
148
149
1099.1.132 by William Grant
Direct port of userservice to the new framework.
150
class Plugin(ViewPlugin):
1294.2.65 by William Grant
Port userservice.
151
    views = [(ApplicationRoot, 'userservice', UserServiceView)]
465 by mattgiuca
Added new app: userservice, which is an ajax service for user management
152
1080.1.90 by William Grant
ivle.rpc.decorators: Add (new package, too). Has a couple of decorators to
153
@require_method('POST')
486 by mattgiuca
caps: Added a few new capabilities.
154
def handle_activate_me(req, fields):
465 by mattgiuca
Added new app: userservice, which is an ajax service for user management
155
    """Create the jail, svn, etc, for the currently logged in user (this is
156
    put in the queue for usermgt to do).
157
    This will block until usermgt returns, which could take seconds to minutes
158
    in the extreme. Therefore, it is designed to be called by Ajax, with a
159
    nice "Please wait" message on the frontend.
160
161
    This will signal that the user has accepted the terms of the license
162
    agreement, and will result in the user's database status being set to
163
    "enabled". (Note that it will be set to "pending" for the duration of the
164
    handling).
165
166
    As such, it takes a single POST field, "declaration", which
167
    must have the value, "I accept the IVLE Terms of Service".
168
    (Otherwise users could navigate to /userservice/createme without
169
    "accepting" the terms - at least this way requires them to acknowledge
170
    their acceptance). It must only be called through a POST request.
171
    """
1099.1.127 by William Grant
Implement ToS acceptance in the new login machinery. Now implemented through
172
1099.1.137 by William Grant
Actually set user in userservice/activate_me.
173
    user = get_user_details(req)
174
486 by mattgiuca
caps: Added a few new capabilities.
175
    try:
1099.1.136 by William Grant
Remove some useless try-except blocks that just raise a 500.
176
        declaration = fields.getfirst('declaration')
177
    except AttributeError:
178
        declaration = None      # Will fail next test
179
    if declaration != USER_DECLARATION:
180
        raise BadRequest()
181
182
    # Make sure the user's status is "no_agreement", and set status to
183
    # pending, within the one transaction. This ensures we only do this
184
    # one time.
185
    try:
186
        # Check that the user's status is "no_agreement".
187
        # (Both to avoid redundant calls, and to stop disabled users from
188
        # re-enabling their accounts).
189
        if user.state != "no_agreement":
190
            raise BadRequest("You have already agreed to the terms.")
191
        # Write state "pending" to ensure we don't try this again
192
        user.state = u"pending"
1080.1.7 by matt.giuca
The new ivle.database.User class is now used in Request and usrmgt, which
193
    except:
194
        req.store.rollback()
195
        raise
1099.1.136 by William Grant
Remove some useless try-except blocks that just raise a 500.
196
    req.store.commit()
197
198
    # Get the arguments for usermgt.activate_user from the session
199
    # (The user must have already logged in to use this app)
200
    args = {
201
        "login": user.login,
202
    }
203
    msg = {'activate_user': args}
204
205
    # Release our lock on the db so usrmgt can write
206
    req.store.rollback()
207
208
    # Try and contact the usrmgt server
209
    try:
1204 by William Grant
Use the config from the request, rather than ivle.conf, in userservice.
210
        response = chat.chat(req.config['usrmgt']['host'],
211
                             req.config['usrmgt']['port'],
212
                             msg,
213
                             req.config['usrmgt']['magic'],
214
                            )
1099.1.136 by William Grant
Remove some useless try-except blocks that just raise a 500.
215
    except cjson.DecodeError:
216
        # Gave back rubbish - set the response to failure
217
        response = {'response': 'usrmgt-failure'}
218
219
    # Get the staus of the users request
220
    try:
221
        status = response['response']
222
    except KeyError:
223
        status = 'failure'
224
225
    if status == 'okay':
226
        user.state = u"enabled"
227
    else:
228
        # Reset the user back to no agreement
229
        user.state = u"no_agreement"
230
231
    # Write the response
232
    req.content_type = "text/plain"
233
    req.write(cjson.encode(response))
486 by mattgiuca
caps: Added a few new capabilities.
234
930 by dcoles
Userservice: Added get_enrolments handler to allow a JSON query of a students
235
def handle_get_enrolments(req, fields):
236
    """
995 by wagrant
common.db: Add get_enrolment_groups. Will return group information for
237
    Retrieve a user's enrolment details. Each enrolment includes any group
238
    memberships for that offering.
930 by dcoles
Userservice: Added get_enrolments handler to allow a JSON query of a students
239
    """
240
    # For the moment we're only able to query ourselves
241
    fullpowers = False
242
243
    try:
1080.1.28 by me at id
www/apps/groups: Use User.active_enrolments rather than ivle.db.get_enrolment.
244
        user = ivle.database.User.get_by_login(req.store,
245
                    fields.getfirst('login'))
246
        if user is None:
930 by dcoles
Userservice: Added get_enrolments handler to allow a JSON query of a students
247
            raise AttributeError()
1080.1.28 by me at id
www/apps/groups: Use User.active_enrolments rather than ivle.db.get_enrolment.
248
        if not fullpowers and user != req.user:
1099.1.134 by William Grant
Replace most userservice req.throw_error()s with new exceptions.
249
            raise Unauthorized()
930 by dcoles
Userservice: Added get_enrolments handler to allow a JSON query of a students
250
    except AttributeError:
251
        # If login not specified, update yourself
1080.1.28 by me at id
www/apps/groups: Use User.active_enrolments rather than ivle.db.get_enrolment.
252
        user = req.user
930 by dcoles
Userservice: Added get_enrolments handler to allow a JSON query of a students
253
1080.1.28 by me at id
www/apps/groups: Use User.active_enrolments rather than ivle.db.get_enrolment.
254
    dict_enrolments = []
255
    for e in user.active_enrolments:
256
        dict_enrolments.append({
257
            'offeringid':      e.offering.id,
1475 by William Grant
Give specialhome a link to each offering index.
258
            'url':             req.publisher.generate(e.offering),
1080.1.28 by me at id
www/apps/groups: Use User.active_enrolments rather than ivle.db.get_enrolment.
259
            'subj_code':       e.offering.subject.code,
260
            'subj_name':       e.offering.subject.name,
261
            'subj_short_name': e.offering.subject.short_name,
262
            'year':            e.offering.semester.year,
263
            'semester':        e.offering.semester.semester,
1080.1.81 by William Grant
ivle.database.Enrolment: Add a groups attribute, containing groups of which
264
            'groups':          [{'name': group.name,
265
                                 'nick': group.nick} for group in e.groups]
1080.1.28 by me at id
www/apps/groups: Use User.active_enrolments rather than ivle.db.get_enrolment.
266
        })
267
    response = cjson.encode(dict_enrolments)
930 by dcoles
Userservice: Added get_enrolments handler to allow a JSON query of a students
268
    req.content_type = "text/plain"
269
    req.write(response)
270
1001 by dcoles
Groups: Added the view half of the group admin panel. This lets people with the
271
def handle_get_project_groups(req, fields):
272
    """Required cap: None
273
    Returns all the project groups in an offering grouped by project set
274
    Required:
275
        offeringid
276
    """
277
278
    offeringid = fields.getfirst('offeringid')
279
    if offeringid is None:
1099.1.134 by William Grant
Replace most userservice req.throw_error()s with new exceptions.
280
        raise BadRequest("Required: offeringid")
1001 by dcoles
Groups: Added the view half of the group admin panel. This lets people with the
281
    try:
282
        offeringid = int(offeringid)
283
    except:
1099.1.134 by William Grant
Replace most userservice req.throw_error()s with new exceptions.
284
        raise BadRequest("offeringid must be an integer")
1080.1.76 by William Grant
ivle.database.Offering: Add project_sets referenceset.
285
286
    offering = req.store.get(ivle.database.Offering, offeringid)
287
1606 by William Grant
Restrict privileges on group-related userservice actions to users with admin_groups on the offering.
288
    if 'admin_groups' not in offering.get_permissions(req.user, req.config):
289
        raise Unauthorized()
290
1080.1.76 by William Grant
ivle.database.Offering: Add project_sets referenceset.
291
    dict_projectsets = []
1099.1.136 by William Grant
Remove some useless try-except blocks that just raise a 500.
292
    for p in offering.project_sets:
293
        dict_projectsets.append({
294
            'projectsetid': p.id,
295
            'max_students_per_group': p.max_students_per_group,
296
            'groups': [{'groupid': g.id,
297
                        'groupnm': g.name,
298
                        'nick': g.nick} for g in p.project_groups]
299
        })
1001 by dcoles
Groups: Added the view half of the group admin panel. This lets people with the
300
1080.1.76 by William Grant
ivle.database.Offering: Add project_sets referenceset.
301
    response = cjson.encode(dict_projectsets)
1001 by dcoles
Groups: Added the view half of the group admin panel. This lets people with the
302
    req.write(response)
303
1080.1.90 by William Grant
ivle.rpc.decorators: Add (new package, too). Has a couple of decorators to
304
@require_method('POST')
986 by dcoles
Groups: Added userservice/create_group call. You can now create a project in
305
def handle_create_group(req, fields):
306
    """Required cap: CAP_MANAGEGROUPS
307
    Creates a project group in a specific project set
308
    Required:
309
        projectsetid, groupnm
310
    Optional:
311
        nick
312
    Returns:
313
        groupid
314
    """
315
    # Get required fields
1080.1.80 by William Grant
www/apps/userservice: Port create_group to Storm.
316
    projectsetid = fields.getfirst('projectsetid').value
317
    groupnm = fields.getfirst('groupnm').value
1606.1.4 by William Grant
Display a nice error when trying to add a group with an invalid name.
318
986 by dcoles
Groups: Added userservice/create_group call. You can now create a project in
319
    if projectsetid is None or groupnm is None:
1099.1.134 by William Grant
Replace most userservice req.throw_error()s with new exceptions.
320
        raise BadRequest("Required: projectsetid, groupnm")
1080.1.80 by William Grant
www/apps/userservice: Port create_group to Storm.
321
    groupnm = unicode(groupnm)
1004 by dcoles
Groups: Now you can add people to a group as well as creating groups from the
322
    try:
323
        projectsetid = int(projectsetid)
324
    except:
1099.1.134 by William Grant
Replace most userservice req.throw_error()s with new exceptions.
325
        raise BadRequest("projectsetid must be an integer")
326
1606.1.4 by William Grant
Display a nice error when trying to add a group with an invalid name.
327
    if not VALID_URL_NAME.match(groupnm):
328
        raise BadRequest(
329
            "Group names must consist of an alphanumeric character followed "
330
            "by any number of alphanumerics, ., + or -.")
331
1606 by William Grant
Restrict privileges on group-related userservice actions to users with admin_groups on the offering.
332
    projectset = req.store.get(ivle.database.ProjectSet, projectsetid)
333
    if projectset is None:
334
        raise BadRequest("Invalid projectsetid")
335
    if 'admin_groups' not in projectset.offering.get_permissions(
336
        req.user, req.config):
337
        raise Unauthorized()
338
986 by dcoles
Groups: Added userservice/create_group call. You can now create a project in
339
    # Get optional fields
1080.1.80 by William Grant
www/apps/userservice: Port create_group to Storm.
340
    nick = fields.getfirst('nick').value
341
    if nick is not None:
342
        nick = unicode(nick)
1006 by dcoles
Groups: Use db transactions for risky usermanagement operations. (Such as when
343
1099.1.136 by William Grant
Remove some useless try-except blocks that just raise a 500.
344
    group = ivle.database.ProjectGroup(name=groupnm,
1606 by William Grant
Restrict privileges on group-related userservice actions to users with admin_groups on the offering.
345
                                       project_set=projectset,
1099.1.136 by William Grant
Remove some useless try-except blocks that just raise a 500.
346
                                       nick=nick,
347
                                       created_by=req.user,
348
                                       epoch=datetime.datetime.now())
349
    req.store.add(group)
350
351
    # Create the group repository
352
    # Yes, this is ugly, and it would be nice to just pass in the groupid,
353
    # but the object isn't visible to the extra transaction in
354
    # usrmgt-server until we commit, which we only do once the repo is
355
    # created.
356
    offering = group.project_set.offering
357
358
    args = {
359
        "subj_short_name": offering.subject.short_name,
360
        "year": offering.semester.year,
361
        "semester": offering.semester.semester,
362
        "groupnm": group.name,
363
    }
364
    msg = {'create_group_repository': args}
365
366
    # Contact the usrmgt server
986 by dcoles
Groups: Added userservice/create_group call. You can now create a project in
367
    try:
1204 by William Grant
Use the config from the request, rather than ivle.conf, in userservice.
368
        usrmgt = chat.chat(req.config['usrmgt']['host'],
369
                           req.config['usrmgt']['port'],
370
                           msg,
371
                           req.config['usrmgt']['magic'],
372
                          )
1099.1.136 by William Grant
Remove some useless try-except blocks that just raise a 500.
373
    except cjson.DecodeError, e:
374
        raise Exception("Could not understand usrmgt server response:" +
375
                        e.message)
376
377
    if 'response' not in usrmgt or usrmgt['response']=='failure':
378
        raise Exception("Failure creating repository: " + str(usrmgt))
986 by dcoles
Groups: Added userservice/create_group call. You can now create a project in
379
380
    req.content_type = "text/plain"
1080.1.80 by William Grant
www/apps/userservice: Port create_group to Storm.
381
    req.write('')
981 by dcoles
Groups: Added in support for creating groups in the database through
382
1004 by dcoles
Groups: Now you can add people to a group as well as creating groups from the
383
def handle_get_group_membership(req, fields):
384
    """ Required cap: None
385
    Returns two lists. One of people in the group and one of people not in the 
386
    group (but enroled in the offering)
387
    Required:
388
        groupid, offeringid
389
    """
390
    # Get required fields
391
    groupid = fields.getfirst('groupid')
392
    offeringid = fields.getfirst('offeringid')
393
    if groupid is None or offeringid is None:
1099.1.134 by William Grant
Replace most userservice req.throw_error()s with new exceptions.
394
        raise BadRequest("Required: groupid, offeringid")
1004 by dcoles
Groups: Now you can add people to a group as well as creating groups from the
395
    try:
396
        groupid = int(groupid)
397
    except:
1099.1.134 by William Grant
Replace most userservice req.throw_error()s with new exceptions.
398
        raise BadRequest("groupid must be an integer")
1080.1.79 by William Grant
ivle.database.Offering: Add a members ReferenceSet.
399
    group = req.store.get(ivle.database.ProjectGroup, groupid)
400
1004 by dcoles
Groups: Now you can add people to a group as well as creating groups from the
401
    try:
402
        offeringid = int(offeringid)
403
    except:
1099.1.134 by William Grant
Replace most userservice req.throw_error()s with new exceptions.
404
        raise BadRequest("offeringid must be an integer")
1080.1.79 by William Grant
ivle.database.Offering: Add a members ReferenceSet.
405
    offering = req.store.get(ivle.database.Offering, offeringid)
406
1606 by William Grant
Restrict privileges on group-related userservice actions to users with admin_groups on the offering.
407
    if 'admin_groups' not in offering.get_permissions(req.user, req.config):
408
        raise Unauthorized()
1080.1.79 by William Grant
ivle.database.Offering: Add a members ReferenceSet.
409
410
    offeringmembers = [{'login': user.login,
411
                        'fullname': user.fullname
412
                       } for user in offering.members.order_by(
413
                            ivle.database.User.login)
414
                      ]
415
    groupmembers = [{'login': user.login,
416
                        'fullname': user.fullname
417
                       } for user in group.members.order_by(
418
                            ivle.database.User.login)
419
                      ]
420
1004 by dcoles
Groups: Now you can add people to a group as well as creating groups from the
421
    # Make sure we don't include members in both lists
422
    for member in groupmembers:
423
        if member in offeringmembers:
424
            offeringmembers.remove(member)
425
426
    response = cjson.encode(
427
        {'groupmembers': groupmembers, 'available': offeringmembers})
428
429
    req.content_type = "text/plain"
430
    req.write(response)
431
1080.1.90 by William Grant
ivle.rpc.decorators: Add (new package, too). Has a couple of decorators to
432
@require_method('POST')
989 by dcoles
Groups: Added userservice/assign_group call. This allows a user with the
433
def handle_assign_group(req, fields):
434
    """ Required cap: CAP_MANAGEGROUPS
435
    Assigns a user to a project group
436
    Required:
437
        login, groupid
438
    """
439
    # Get required fields
440
    login = fields.getfirst('login')
441
    groupid = fields.getfirst('groupid')
442
    if login is None or groupid is None:
1099.1.134 by William Grant
Replace most userservice req.throw_error()s with new exceptions.
443
        raise BadRequest("Required: login, groupid")
1080.1.84 by William Grant
wwww/apps/userservice: Create group memberships using Storm, not ivle.db.
444
445
    group = req.store.get(ivle.database.ProjectGroup, int(groupid))
446
    user = ivle.database.User.get_by_login(req.store, login)
447
1606 by William Grant
Restrict privileges on group-related userservice actions to users with admin_groups on the offering.
448
    if 'admin_groups' not in group.project_set.offering.get_permissions(
449
        req.user, req.config):
450
        raise Unauthorized()
451
1080.1.84 by William Grant
wwww/apps/userservice: Create group memberships using Storm, not ivle.db.
452
    # Add membership to database
453
    # We can't keep a transaction open until the end here, as usrmgt-server
454
    # needs to see the changes!
1099.1.136 by William Grant
Remove some useless try-except blocks that just raise a 500.
455
    group.members.add(user)
456
    req.store.commit()
457
458
    # Rebuild the svn config file
459
    # Contact the usrmgt server
460
    msg = {'rebuild_svn_group_config': {}}
989 by dcoles
Groups: Added userservice/assign_group call. This allows a user with the
461
    try:
1204 by William Grant
Use the config from the request, rather than ivle.conf, in userservice.
462
        usrmgt = chat.chat(req.config['usrmgt']['host'],
463
                           req.config['usrmgt']['port'],
464
                           msg,
465
                           req.config['usrmgt']['magic'],
466
                          )
1099.1.136 by William Grant
Remove some useless try-except blocks that just raise a 500.
467
    except cjson.DecodeError, e:
468
        raise Exception("Could not understand usrmgt server response: %s" +
469
                        e.message)
470
471
        if 'response' not in usrmgt or usrmgt['response']=='failure':
472
            raise Exception("Failure creating repository: " + str(usrmgt))
989 by dcoles
Groups: Added userservice/assign_group call. This allows a user with the
473
474
    return(cjson.encode({'response': 'okay'}))
981 by dcoles
Groups: Added in support for creating groups in the database through
475
1181 by William Grant
Allow revocation of group memberships by tutors and lecturers.
476
@require_method('POST')
477
def handle_unassign_group(req, fields):
478
    """Remove a user from a project group.
479
480
    The user is removed from the group in the database, and access to the
481
    group Subversion repository is revoked.
482
483
    Note that any checkouts will remain, although they will be unusable.
484
    """
485
486
    # Get required fields
487
    login = fields.getfirst('login')
488
    groupid = fields.getfirst('groupid')
489
    if login is None or groupid is None:
490
        raise BadRequest("Required: login, groupid")
491
492
    group = req.store.get(ivle.database.ProjectGroup, int(groupid))
493
    user = ivle.database.User.get_by_login(req.store, login)
494
1606 by William Grant
Restrict privileges on group-related userservice actions to users with admin_groups on the offering.
495
    if 'admin_groups' not in group.project_set.offering.get_permissions(
496
        req.user, req.config):
497
        raise Unauthorized()
498
1181 by William Grant
Allow revocation of group memberships by tutors and lecturers.
499
    # Remove membership from the database
500
    # We can't keep a transaction open until the end here, as usrmgt-server
501
    # needs to see the changes!
502
    group.members.remove(user)
503
    req.store.commit()
504
505
    # Rebuild the svn config file
506
    # Contact the usrmgt server
507
    msg = {'rebuild_svn_group_config': {}}
508
    try:
1204 by William Grant
Use the config from the request, rather than ivle.conf, in userservice.
509
        usrmgt = chat.chat(req.config['usrmgt']['host'],
510
                           req.config['usrmgt']['port'],
511
                           msg,
512
                           req.config['usrmgt']['magic'],
513
                          )
1181 by William Grant
Allow revocation of group memberships by tutors and lecturers.
514
    except cjson.DecodeError, e:
515
        raise Exception("Could not understand usrmgt server response: %s" +
516
                        e.message)
517
518
        if 'response' not in usrmgt or usrmgt['response']=='failure':
519
            raise Exception("Failure creating repository: " + str(usrmgt))
520
521
    return(cjson.encode({'response': 'okay'}))
522
536 by mattgiuca
apps/userservice: Generalised searching for actions (dict mapping action names to
523
# Map action names (from the path)
524
# to actual function objects
525
actions_map = {
526
    "activate_me": handle_activate_me,
930 by dcoles
Userservice: Added get_enrolments handler to allow a JSON query of a students
527
    "get_enrolments": handle_get_enrolments,
1001 by dcoles
Groups: Added the view half of the group admin panel. This lets people with the
528
    "get_project_groups": handle_get_project_groups,
1004 by dcoles
Groups: Now you can add people to a group as well as creating groups from the
529
    "get_group_membership": handle_get_group_membership,
986 by dcoles
Groups: Added userservice/create_group call. You can now create a project in
530
    "create_group": handle_create_group,
989 by dcoles
Groups: Added userservice/assign_group call. This allows a user with the
531
    "assign_group": handle_assign_group,
1181 by William Grant
Allow revocation of group memberships by tutors and lecturers.
532
    "unassign_group": handle_unassign_group,
536 by mattgiuca
apps/userservice: Generalised searching for actions (dict mapping action names to
533
}