Revision a7bdef13

b/pithos/im/target/shibboleth.py
67 67
    
68 68
    affiliation = tokens.get(Tokens.SHIB_EP_AFFILIATION, '')
69 69
    
70
    if settings.FORCE_PROFILE_UPDATE and not user.is_verified:
71
        params = urlencode({'next': next})
72
        next = reverse('pithos.im.views.users_profile', args=(user.id,))
73
        next = next + '?' + params
74
    
75 70
    return prepare_response(request,
76 71
                            get_or_create_user(eppn, realname, affiliation, 0),
77 72
                            request.GET.get('next'),
b/pithos/im/views.py
34 34
import json
35 35
import logging
36 36
import socket
37
import csv
38 37

  
39 38
from datetime import datetime
40 39
from functools import wraps
......
76 75
        return func(request, *args)
77 76
    return wrapper
78 77

  
78

  
79 79
def requires_admin(func):
80 80
    @wraps(func)
81 81
    def wrapper(request, *args):
......
91 91

  
92 92

  
93 93
def index(request):
94
    kwargs = {'standard_modules':settings.IM_STANDARD_MODULES,
95
              'other_modules':settings.IM_OTHER_MODULES}
96
    return render_response('index.html',
97
                           next=request.GET.get('next', ''),
98
                           **kwargs)
94
    return render_response('index.html', next=request.GET.get('next', ''))
99 95

  
100 96

  
101 97
@requires_admin
......
138 134
                            page=page,
139 135
                            prev=prev,
140 136
                            next=next)
137
    
138
@requires_admin
139
def users_create(request):
140
    if request.method == 'GET':
141
        return render_response('users_create.html')
142
    if request.method == 'POST':
143
        user = User()
144
        user.uniq = request.POST.get('uniq')
145
        user.realname = request.POST.get('realname')
146
        user.is_admin = True if request.POST.get('admin') else False
147
        user.affiliation = request.POST.get('affiliation')
148
        user.quota = int(request.POST.get('quota') or 0) * (1024 ** 3)  # In GiB
149
        user.renew_token()
150
        user.save()
151
        return redirect(users_info, user.id)
141 152

  
142 153
@requires_admin
143 154
def users_info(request, user_id):
......
218 229
                defaults={'code': code, 'realname': realname})
219 230
            
220 231
            try:
221
                send_invitation(request.build_absolute_uri('/').rstrip('/'), invitation)
232
                send_invitation(request.get_host(), invitation)
222 233
                if created:
223 234
                    inviter.invitations = max(0, inviter.invitations - 1)
224 235
                    inviter.save()
......
246 257
    return HttpResponse(html)
247 258

  
248 259
def send_verification(baseurl, user):
260
    next = quote('http://%s' % baseurl)
249 261
    url = settings.ACTIVATION_LOGIN_TARGET % (baseurl,
250 262
                                              quote(user.auth_token),
251
                                              quote(baseurl))
263
                                              next)
252 264
    message = render_to_string('activation.txt', {
253 265
            'user': user,
254 266
            'url': url,
......
297 309
                user.level = 1
298 310
                user.renew_token()
299 311
                try:
300
                    send_verification(request.build_absolute_uri('/').rstrip('/'), user)
312
                    send_verification(request.get_host(), user)
301 313
                    message = _('Verification sent to %s' % user.email)
302 314
                    user.save()
303 315
                except (SMTPException, socket.error) as e:
......
312 324
        return response
313 325

  
314 326
def send_password(baseurl, user):
327
    next = quote('http://%s' % baseurl)
315 328
    url = settings.PASSWORD_RESET_TARGET % (baseurl,
316 329
                                            quote(user.uniq),
317
                                            quote(baseurl))
330
                                            next)
318 331
    message = render_to_string('password.txt', {
319 332
            'user': user,
320 333
            'url': url,
......
334 347
        try:
335 348
            user = User.objects.get(uniq=username)
336 349
            try:
337
                send_password(request.build_absolute_uri('/').rstrip('/'), user)
350
                send_password(request.get_host(), user)
338 351
                status = 'success'
339 352
                message = _('Password reset sent to %s' % user.email)
340 353
                user.status = 'UNVERIFIED'
......
351 364
                'status': status,
352 365
                'message': message})
353 366
        return HttpResponse(html)
354

  
355
@requires_admin
356
def invitations_list(request):
357
    invitations = Invitation.objects.order_by('id')
358
    
359
    filter = request.GET.get('filter', '')
360
    if filter:
361
        if filter.startswith('-'):
362
            invitations = invitations.exclude(uniq__icontains=filter[1:])
363
        else:
364
            invitations = invitations.filter(uniq__icontains=filter)
365
    
366
    try:
367
        page = int(request.GET.get('page', 1))
368
    except ValueError:
369
        page = 1
370
    offset = max(0, page - 1) * settings.ADMIN_PAGE_LIMIT
371
    limit = offset + settings.ADMIN_PAGE_LIMIT
372
    
373
    npages = int(ceil(1.0 * invitations.count() / settings.ADMIN_PAGE_LIMIT))
374
    prev = page - 1 if page > 1 else None
375
    next = page + 1 if page < npages else None
376
    return render_response('invitations_list.html',
377
                            invitations=invitations[offset:limit],
378
                            filter=filter,
379
                            pages=range(1, npages + 1),
380
                            page=page,
381
                            prev=prev,
382
                            next=next)
383

  
384
@requires_admin
385
def invitations_export(request):
386
    # Create the HttpResponse object with the appropriate CSV header.
387
    response = HttpResponse(mimetype='text/csv')
388
    response['Content-Disposition'] = 'attachment; filename=invitations.csv'
389

  
390
    writer = csv.writer(response)
391
    writer.writerow(['ID',
392
                     'Uniq',
393
                     'Real Name',
394
                     'Code',
395
                     'Inviter Uniq',
396
                     'Inviter Real Name',
397
                     'Is_accepted',
398
                     'Created',
399
                     'Accepted',])
400
    invitations = Invitation.objects.order_by('id')
401
    for inv in invitations:
402
        writer.writerow([inv.id,
403
                         inv.uniq.encode("utf-8"),
404
                         inv.realname.encode("utf-8"),
405
                         inv.code,
406
                         inv.inviter.uniq.encode("utf-8"),
407
                         inv.inviter.realname.encode("utf-8"),
408
                         inv.is_accepted,
409
                         inv.created,
410
                         inv.accepted])
411

  
412
    return response
413

  
414

  
415
@requires_admin
416
def users_export(request):
417
    # Create the HttpResponse object with the appropriate CSV header.
418
    response = HttpResponse(mimetype='text/csv')
419
    response['Content-Disposition'] = 'attachment; filename=users.csv'
420

  
421
    writer = csv.writer(response)
422
    writer.writerow(['ID',
423
                     'Uniq',
424
                     'Real Name',
425
                     'Admin',
426
                     'Affiliation',
427
                     'State',
428
                     'Quota (GiB)',
429
                     'Updated',])
430
    users = User.objects.order_by('id')
431
    for u in users:
432
        writer.writerow([u.id,
433
                         u.uniq.encode("utf-8"),
434
                         u.realname.encode("utf-8"),
435
                         u.is_admin,
436
                         u.affiliation.encode("utf-8"),
437
                         u.state.encode("utf-8"),
438
                         u.quota,
439
                         u.updated])
440

  
441
    return response
442

  
443
@requires_admin
444
def users_create(request):
445
    if request.method == 'GET':
446
        return render_response('users_create.html')
447
    if request.method == 'POST':
448
        user = User()
449
        user.uniq = request.POST.get('uniq')
450
        user.realname = request.POST.get('realname')
451
        user.is_admin = True if request.POST.get('admin') else False
452
        user.affiliation = request.POST.get('affiliation')
453
        user.quota = int(request.POST.get('quota') or 0) * (1024 ** 3)  # In GiB
454
        user.renew_token()
455
        user.save()
456
        return redirect(users_info, user.id)
457

  
458
@requires_login
459
def users_profile(request):
460
    next = request.GET.get('next')
461
    user = User.objects.get(uniq=request.user)
462
    states = [x[0] for x in User.ACCOUNT_STATE]
463
    return render_response('users_profile.html',
464
                            user=user,
465
                            states=states,
466
                            next=next)
467

  
468
@requires_login
469
def users_edit(request):
470
    user = User.objects.get(uniq=request.user)
471
    user.realname = request.POST.get('realname')
472
    user.affiliation = request.POST.get('affiliation')
473
    user.is_verified = True
474
    user.save()
475
    next = request.POST.get('next')
476
    if next:
477
        return redirect(next)
478
    
479
    status = 'success'
480
    message = _('Profile has been updated')
481
    html = render_to_string('users_profile.html', {
482
            'user': user,
483
            'status': status,
484
            'message': message})
485
    return HttpResponse(html)
486
    
b/pithos/settings.py.dist
74 74
BACKEND_BLOCK_MODULE = 'pithos.backends.lib.hashfiler'
75 75
BACKEND_BLOCK_PATH = os.path.join(PROJECT_PATH, 'data/')
76 76

  
77

  
77 78
# Bypass authentication for user administration.
78 79
BYPASS_ADMIN_AUTH = False
79 80

  
......
130 131
#    'django.middleware.csrf.CsrfViewMiddleware',
131 132
#    'django.contrib.auth.middleware.AuthenticationMiddleware',
132 133
#    'django.contrib.messages.middleware.MessageMiddleware',
133
    'pithos.middleware.URLEncodedHeadersMiddleware',
134 134
    'pithos.middleware.LoggingConfigMiddleware',
135 135
    'pithos.middleware.AuthMiddleware'
136 136
)
......
153 153
#    'django.contrib.admindocs',
154 154
    'pithos.im',
155 155
    'pithos.api',
156
    'pithos.ui',
157
    'south'
156
    'pithos.public',
157
    'pithos.ui'
158 158
)
159 159

  
160 160
# Set the expiration time of newly created auth tokens
......
187 187
    4   :   0
188 188
}
189 189

  
190
SERVICE_NAME = 'Pithos'
190
SERVICE_NAME = 'Pithos+'
191 191

  
192 192
# Where users should login with their invitation code
193
INVITATION_LOGIN_TARGET = '%s/im/login/invitation' \
193
INVITATION_LOGIN_TARGET = 'http://%s/im/login/invitation' \
194 194
                            '?code=%d' \
195 195
                            '&next=%s'
196 196

  
197 197
# Where users should activate their local account
198
ACTIVATION_LOGIN_TARGET = '%s/im/local/activate/' \
198
ACTIVATION_LOGIN_TARGET = 'http://%s/im/local/activate/' \
199 199
                            '?auth=%s' \
200 200
                            '&next=%s'
201 201

  
202 202
# Where users should reset their local password
203
PASSWORD_RESET_TARGET = '%s/im/local/reset/' \
203
PASSWORD_RESET_TARGET = 'http://%s/im/local/reset/' \
204 204
                            '?username=%s' \
205 205
                            '&next=%s'
206 206

  
207
# Force user profile verification
208
FORCE_PROFILE_UPDATE = False
209

  
210 207
# The server is behind a proy (apache and gunicorn setup).
211 208
USE_X_FORWARDED_HOST = False
212 209

  
213
# Set umask (needed for gunicorn setup).
214
#os.umask(0077)
215

  
216 210
# Use to log to a file.
217 211
LOGFILE = None
218

  
219
# Identity Management enabled modules
220
IM_STANDARD_MODULES = ['local', 'invitation']
221
IM_OTHER_MODULES = ['twitter', 'shibboleth']

Also available in: Unified diff