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