~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,
1686 by Matt Giuca
Specialhome now shows the semester name for any non-current offerings. userservice/get_enrolments now returns the 'state' property of an offering (past, current, future, etc). This fixes Launchpad bug #526838.
264
            'state':           e.offering.semester.state,
1080.1.81 by William Grant
ivle.database.Enrolment: Add a groups attribute, containing groups of which
265
            'groups':          [{'name': group.name,
266
                                 '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.
267
        })
268
    response = cjson.encode(dict_enrolments)
930 by dcoles
Userservice: Added get_enrolments handler to allow a JSON query of a students
269
    req.content_type = "text/plain"
270
    req.write(response)
271
1001 by dcoles
Groups: Added the view half of the group admin panel. This lets people with the
272
def handle_get_project_groups(req, fields):
273
    """Required cap: None
274
    Returns all the project groups in an offering grouped by project set
275
    Required:
276
        offeringid
277
    """
278
279
    offeringid = fields.getfirst('offeringid')
280
    if offeringid is None:
1099.1.134 by William Grant
Replace most userservice req.throw_error()s with new exceptions.
281
        raise BadRequest("Required: offeringid")
1001 by dcoles
Groups: Added the view half of the group admin panel. This lets people with the
282
    try:
283
        offeringid = int(offeringid)
284
    except:
1099.1.134 by William Grant
Replace most userservice req.throw_error()s with new exceptions.
285
        raise BadRequest("offeringid must be an integer")
1080.1.76 by William Grant
ivle.database.Offering: Add project_sets referenceset.
286
287
    offering = req.store.get(ivle.database.Offering, offeringid)
288
1606 by William Grant
Restrict privileges on group-related userservice actions to users with admin_groups on the offering.
289
    if 'admin_groups' not in offering.get_permissions(req.user, req.config):
290
        raise Unauthorized()
291
1080.1.76 by William Grant
ivle.database.Offering: Add project_sets referenceset.
292
    dict_projectsets = []
1099.1.136 by William Grant
Remove some useless try-except blocks that just raise a 500.
293
    for p in offering.project_sets:
294
        dict_projectsets.append({
295
            'projectsetid': p.id,
296
            'max_students_per_group': p.max_students_per_group,
297
            'groups': [{'groupid': g.id,
298
                        'groupnm': g.name,
299
                        'nick': g.nick} for g in p.project_groups]
300
        })
1001 by dcoles
Groups: Added the view half of the group admin panel. This lets people with the
301
1080.1.76 by William Grant
ivle.database.Offering: Add project_sets referenceset.
302
    response = cjson.encode(dict_projectsets)
1001 by dcoles
Groups: Added the view half of the group admin panel. This lets people with the
303
    req.write(response)
304
1080.1.90 by William Grant
ivle.rpc.decorators: Add (new package, too). Has a couple of decorators to
305
@require_method('POST')
986 by dcoles
Groups: Added userservice/create_group call. You can now create a project in
306
def handle_create_group(req, fields):
307
    """Required cap: CAP_MANAGEGROUPS
308
    Creates a project group in a specific project set
309
    Required:
310
        projectsetid, groupnm
311
    Optional:
312
        nick
313
    Returns:
314
        groupid
315
    """
316
    # Get required fields
1080.1.80 by William Grant
www/apps/userservice: Port create_group to Storm.
317
    projectsetid = fields.getfirst('projectsetid').value
318
    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.
319
986 by dcoles
Groups: Added userservice/create_group call. You can now create a project in
320
    if projectsetid is None or groupnm is None:
1099.1.134 by William Grant
Replace most userservice req.throw_error()s with new exceptions.
321
        raise BadRequest("Required: projectsetid, groupnm")
1080.1.80 by William Grant
www/apps/userservice: Port create_group to Storm.
322
    groupnm = unicode(groupnm)
1004 by dcoles
Groups: Now you can add people to a group as well as creating groups from the
323
    try:
324
        projectsetid = int(projectsetid)
325
    except:
1099.1.134 by William Grant
Replace most userservice req.throw_error()s with new exceptions.
326
        raise BadRequest("projectsetid must be an integer")
327
1606.1.4 by William Grant
Display a nice error when trying to add a group with an invalid name.
328
    if not VALID_URL_NAME.match(groupnm):
329
        raise BadRequest(
1606.1.16 by William Grant
Fix validation messages to inform users that alphanumerics in names must be lowercase.
330
            "Group names must consist of a lowercase alphanumeric character "
331
            "followed by any number of lowercase alphanumerics, ., +, - or _.")
1606.1.4 by William Grant
Display a nice error when trying to add a group with an invalid name.
332
1606 by William Grant
Restrict privileges on group-related userservice actions to users with admin_groups on the offering.
333
    projectset = req.store.get(ivle.database.ProjectSet, projectsetid)
334
    if projectset is None:
335
        raise BadRequest("Invalid projectsetid")
1713 by Matt Giuca
userservice: No longer allows creation of a project group for a solo projectset. This addresses Launchpad bug #527543.
336
    if not projectset.is_group:
337
        raise BadRequest("Not a group project set")
1606 by William Grant
Restrict privileges on group-related userservice actions to users with admin_groups on the offering.
338
    if 'admin_groups' not in projectset.offering.get_permissions(
339
        req.user, req.config):
340
        raise Unauthorized()
341
986 by dcoles
Groups: Added userservice/create_group call. You can now create a project in
342
    # Get optional fields
1080.1.80 by William Grant
www/apps/userservice: Port create_group to Storm.
343
    nick = fields.getfirst('nick').value
344
    if nick is not None:
345
        nick = unicode(nick)
1006 by dcoles
Groups: Use db transactions for risky usermanagement operations. (Such as when
346
1099.1.136 by William Grant
Remove some useless try-except blocks that just raise a 500.
347
    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.
348
                                       project_set=projectset,
1099.1.136 by William Grant
Remove some useless try-except blocks that just raise a 500.
349
                                       nick=nick,
350
                                       created_by=req.user,
351
                                       epoch=datetime.datetime.now())
352
    req.store.add(group)
353
354
    # Create the group repository
355
    # Yes, this is ugly, and it would be nice to just pass in the groupid,
356
    # but the object isn't visible to the extra transaction in
357
    # usrmgt-server until we commit, which we only do once the repo is
358
    # created.
359
    offering = group.project_set.offering
360
361
    args = {
362
        "subj_short_name": offering.subject.short_name,
363
        "year": offering.semester.year,
364
        "semester": offering.semester.semester,
365
        "groupnm": group.name,
366
    }
367
    msg = {'create_group_repository': args}
368
369
    # Contact the usrmgt server
986 by dcoles
Groups: Added userservice/create_group call. You can now create a project in
370
    try:
1204 by William Grant
Use the config from the request, rather than ivle.conf, in userservice.
371
        usrmgt = chat.chat(req.config['usrmgt']['host'],
372
                           req.config['usrmgt']['port'],
373
                           msg,
374
                           req.config['usrmgt']['magic'],
375
                          )
1099.1.136 by William Grant
Remove some useless try-except blocks that just raise a 500.
376
    except cjson.DecodeError, e:
377
        raise Exception("Could not understand usrmgt server response:" +
378
                        e.message)
379
380
    if 'response' not in usrmgt or usrmgt['response']=='failure':
381
        raise Exception("Failure creating repository: " + str(usrmgt))
986 by dcoles
Groups: Added userservice/create_group call. You can now create a project in
382
383
    req.content_type = "text/plain"
1080.1.80 by William Grant
www/apps/userservice: Port create_group to Storm.
384
    req.write('')
981 by dcoles
Groups: Added in support for creating groups in the database through
385
1004 by dcoles
Groups: Now you can add people to a group as well as creating groups from the
386
def handle_get_group_membership(req, fields):
387
    """ Required cap: None
388
    Returns two lists. One of people in the group and one of people not in the 
389
    group (but enroled in the offering)
390
    Required:
391
        groupid, offeringid
392
    """
393
    # Get required fields
394
    groupid = fields.getfirst('groupid')
395
    offeringid = fields.getfirst('offeringid')
396
    if groupid is None or offeringid is None:
1099.1.134 by William Grant
Replace most userservice req.throw_error()s with new exceptions.
397
        raise BadRequest("Required: groupid, offeringid")
1004 by dcoles
Groups: Now you can add people to a group as well as creating groups from the
398
    try:
399
        groupid = int(groupid)
400
    except:
1099.1.134 by William Grant
Replace most userservice req.throw_error()s with new exceptions.
401
        raise BadRequest("groupid must be an integer")
1080.1.79 by William Grant
ivle.database.Offering: Add a members ReferenceSet.
402
    group = req.store.get(ivle.database.ProjectGroup, groupid)
403
1004 by dcoles
Groups: Now you can add people to a group as well as creating groups from the
404
    try:
405
        offeringid = int(offeringid)
406
    except:
1099.1.134 by William Grant
Replace most userservice req.throw_error()s with new exceptions.
407
        raise BadRequest("offeringid must be an integer")
1080.1.79 by William Grant
ivle.database.Offering: Add a members ReferenceSet.
408
    offering = req.store.get(ivle.database.Offering, offeringid)
409
1606 by William Grant
Restrict privileges on group-related userservice actions to users with admin_groups on the offering.
410
    if 'admin_groups' not in offering.get_permissions(req.user, req.config):
411
        raise Unauthorized()
1080.1.79 by William Grant
ivle.database.Offering: Add a members ReferenceSet.
412
413
    offeringmembers = [{'login': user.login,
414
                        'fullname': user.fullname
415
                       } for user in offering.members.order_by(
416
                            ivle.database.User.login)
417
                      ]
418
    groupmembers = [{'login': user.login,
419
                        'fullname': user.fullname
420
                       } for user in group.members.order_by(
421
                            ivle.database.User.login)
422
                      ]
423
1004 by dcoles
Groups: Now you can add people to a group as well as creating groups from the
424
    # Make sure we don't include members in both lists
425
    for member in groupmembers:
426
        if member in offeringmembers:
427
            offeringmembers.remove(member)
428
429
    response = cjson.encode(
430
        {'groupmembers': groupmembers, 'available': offeringmembers})
431
432
    req.content_type = "text/plain"
433
    req.write(response)
434
1080.1.90 by William Grant
ivle.rpc.decorators: Add (new package, too). Has a couple of decorators to
435
@require_method('POST')
989 by dcoles
Groups: Added userservice/assign_group call. This allows a user with the
436
def handle_assign_group(req, fields):
437
    """ Required cap: CAP_MANAGEGROUPS
438
    Assigns a user to a project group
439
    Required:
440
        login, groupid
441
    """
442
    # Get required fields
443
    login = fields.getfirst('login')
444
    groupid = fields.getfirst('groupid')
445
    if login is None or groupid is None:
1099.1.134 by William Grant
Replace most userservice req.throw_error()s with new exceptions.
446
        raise BadRequest("Required: login, groupid")
1080.1.84 by William Grant
wwww/apps/userservice: Create group memberships using Storm, not ivle.db.
447
448
    group = req.store.get(ivle.database.ProjectGroup, int(groupid))
449
    user = ivle.database.User.get_by_login(req.store, login)
450
1606 by William Grant
Restrict privileges on group-related userservice actions to users with admin_groups on the offering.
451
    if 'admin_groups' not in group.project_set.offering.get_permissions(
452
        req.user, req.config):
453
        raise Unauthorized()
454
1080.1.84 by William Grant
wwww/apps/userservice: Create group memberships using Storm, not ivle.db.
455
    # Add membership to database
456
    # We can't keep a transaction open until the end here, as usrmgt-server
457
    # needs to see the changes!
1099.1.136 by William Grant
Remove some useless try-except blocks that just raise a 500.
458
    group.members.add(user)
459
    req.store.commit()
460
461
    # Rebuild the svn config file
462
    # Contact the usrmgt server
463
    msg = {'rebuild_svn_group_config': {}}
989 by dcoles
Groups: Added userservice/assign_group call. This allows a user with the
464
    try:
1204 by William Grant
Use the config from the request, rather than ivle.conf, in userservice.
465
        usrmgt = chat.chat(req.config['usrmgt']['host'],
466
                           req.config['usrmgt']['port'],
467
                           msg,
468
                           req.config['usrmgt']['magic'],
469
                          )
1099.1.136 by William Grant
Remove some useless try-except blocks that just raise a 500.
470
    except cjson.DecodeError, e:
471
        raise Exception("Could not understand usrmgt server response: %s" +
472
                        e.message)
473
474
        if 'response' not in usrmgt or usrmgt['response']=='failure':
475
            raise Exception("Failure creating repository: " + str(usrmgt))
989 by dcoles
Groups: Added userservice/assign_group call. This allows a user with the
476
477
    return(cjson.encode({'response': 'okay'}))
981 by dcoles
Groups: Added in support for creating groups in the database through
478
1181 by William Grant
Allow revocation of group memberships by tutors and lecturers.
479
@require_method('POST')
480
def handle_unassign_group(req, fields):
481
    """Remove a user from a project group.
482
483
    The user is removed from the group in the database, and access to the
484
    group Subversion repository is revoked.
485
486
    Note that any checkouts will remain, although they will be unusable.
487
    """
488
489
    # Get required fields
490
    login = fields.getfirst('login')
491
    groupid = fields.getfirst('groupid')
492
    if login is None or groupid is None:
493
        raise BadRequest("Required: login, groupid")
494
495
    group = req.store.get(ivle.database.ProjectGroup, int(groupid))
496
    user = ivle.database.User.get_by_login(req.store, login)
497
1606 by William Grant
Restrict privileges on group-related userservice actions to users with admin_groups on the offering.
498
    if 'admin_groups' not in group.project_set.offering.get_permissions(
499
        req.user, req.config):
500
        raise Unauthorized()
501
1181 by William Grant
Allow revocation of group memberships by tutors and lecturers.
502
    # Remove membership from the database
503
    # We can't keep a transaction open until the end here, as usrmgt-server
504
    # needs to see the changes!
505
    group.members.remove(user)
506
    req.store.commit()
507
508
    # Rebuild the svn config file
509
    # Contact the usrmgt server
510
    msg = {'rebuild_svn_group_config': {}}
511
    try:
1204 by William Grant
Use the config from the request, rather than ivle.conf, in userservice.
512
        usrmgt = chat.chat(req.config['usrmgt']['host'],
513
                           req.config['usrmgt']['port'],
514
                           msg,
515
                           req.config['usrmgt']['magic'],
516
                          )
1181 by William Grant
Allow revocation of group memberships by tutors and lecturers.
517
    except cjson.DecodeError, e:
518
        raise Exception("Could not understand usrmgt server response: %s" +
519
                        e.message)
520
521
        if 'response' not in usrmgt or usrmgt['response']=='failure':
522
            raise Exception("Failure creating repository: " + str(usrmgt))
523
524
    return(cjson.encode({'response': 'okay'}))
525
536 by mattgiuca
apps/userservice: Generalised searching for actions (dict mapping action names to
526
# Map action names (from the path)
527
# to actual function objects
528
actions_map = {
529
    "activate_me": handle_activate_me,
930 by dcoles
Userservice: Added get_enrolments handler to allow a JSON query of a students
530
    "get_enrolments": handle_get_enrolments,
1001 by dcoles
Groups: Added the view half of the group admin panel. This lets people with the
531
    "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
532
    "get_group_membership": handle_get_group_membership,
986 by dcoles
Groups: Added userservice/create_group call. You can now create a project in
533
    "create_group": handle_create_group,
989 by dcoles
Groups: Added userservice/assign_group call. This allows a user with the
534
    "assign_group": handle_assign_group,
1181 by William Grant
Allow revocation of group memberships by tutors and lecturers.
535
    "unassign_group": handle_unassign_group,
536 by mattgiuca
apps/userservice: Generalised searching for actions (dict mapping action names to
536
}