import json
import logging
import socket
+import csv
from datetime import datetime
from functools import wraps
return func(request, *args)
return wrapper
-
def requires_admin(func):
@wraps(func)
def wrapper(request, *args):
def index(request):
- return render_response('index.html', next=request.GET.get('next', ''))
+ kwargs = {'standard_modules':settings.IM_STANDARD_MODULES,
+ 'other_modules':settings.IM_OTHER_MODULES}
+ return render_response('index.html',
+ next=request.GET.get('next', ''),
+ **kwargs)
@requires_admin
page=page,
prev=prev,
next=next)
-
-@requires_admin
-def users_create(request):
- if request.method == 'GET':
- return render_response('users_create.html')
- if request.method == 'POST':
- user = User()
- user.uniq = request.POST.get('uniq')
- user.realname = request.POST.get('realname')
- user.is_admin = True if request.POST.get('admin') else False
- user.affiliation = request.POST.get('affiliation')
- user.quota = int(request.POST.get('quota') or 0) * (1024 ** 3) # In GiB
- user.renew_token()
- user.save()
- return redirect(users_info, user.id)
@requires_admin
def users_info(request, user_id):
defaults={'code': code, 'realname': realname})
try:
- send_invitation(request.get_host(), invitation)
+ send_invitation(request.build_absolute_uri('/').rstrip('/'), invitation)
if created:
inviter.invitations = max(0, inviter.invitations - 1)
inviter.save()
return HttpResponse(html)
def send_verification(baseurl, user):
- next = quote('http://%s' % baseurl)
url = settings.ACTIVATION_LOGIN_TARGET % (baseurl,
quote(user.auth_token),
- next)
+ quote(baseurl))
message = render_to_string('activation.txt', {
'user': user,
'url': url,
user.level = 1
user.renew_token()
try:
- send_verification(request.get_host(), user)
+ send_verification(request.build_absolute_uri('/').rstrip('/'), user)
message = _('Verification sent to %s' % user.email)
user.save()
except (SMTPException, socket.error) as e:
return response
def send_password(baseurl, user):
- next = quote('http://%s' % baseurl)
url = settings.PASSWORD_RESET_TARGET % (baseurl,
quote(user.uniq),
- next)
+ quote(baseurl))
message = render_to_string('password.txt', {
'user': user,
'url': url,
try:
user = User.objects.get(uniq=username)
try:
- send_password(request.get_host(), user)
+ send_password(request.build_absolute_uri('/').rstrip('/'), user)
status = 'success'
message = _('Password reset sent to %s' % user.email)
user.status = 'UNVERIFIED'
'status': status,
'message': message})
return HttpResponse(html)
+
+@requires_admin
+def invitations_list(request):
+ invitations = Invitation.objects.order_by('id')
+
+ filter = request.GET.get('filter', '')
+ if filter:
+ if filter.startswith('-'):
+ invitations = invitations.exclude(uniq__icontains=filter[1:])
+ else:
+ invitations = invitations.filter(uniq__icontains=filter)
+
+ try:
+ page = int(request.GET.get('page', 1))
+ except ValueError:
+ page = 1
+ offset = max(0, page - 1) * settings.ADMIN_PAGE_LIMIT
+ limit = offset + settings.ADMIN_PAGE_LIMIT
+
+ npages = int(ceil(1.0 * invitations.count() / settings.ADMIN_PAGE_LIMIT))
+ prev = page - 1 if page > 1 else None
+ next = page + 1 if page < npages else None
+ return render_response('invitations_list.html',
+ invitations=invitations[offset:limit],
+ filter=filter,
+ pages=range(1, npages + 1),
+ page=page,
+ prev=prev,
+ next=next)
+
+@requires_admin
+def invitations_export(request):
+ # Create the HttpResponse object with the appropriate CSV header.
+ response = HttpResponse(mimetype='text/csv')
+ response['Content-Disposition'] = 'attachment; filename=invitations.csv'
+
+ writer = csv.writer(response)
+ writer.writerow(['ID',
+ 'Uniq',
+ 'Real Name',
+ 'Code',
+ 'Inviter Uniq',
+ 'Inviter Real Name',
+ 'Is_accepted',
+ 'Created',
+ 'Accepted',])
+ invitations = Invitation.objects.order_by('id')
+ for inv in invitations:
+ writer.writerow([inv.id,
+ inv.uniq.encode("utf-8"),
+ inv.realname.encode("utf-8"),
+ inv.code,
+ inv.inviter.uniq.encode("utf-8"),
+ inv.inviter.realname.encode("utf-8"),
+ inv.is_accepted,
+ inv.created,
+ inv.accepted])
+
+ return response
+
+
+@requires_admin
+def users_export(request):
+ # Create the HttpResponse object with the appropriate CSV header.
+ response = HttpResponse(mimetype='text/csv')
+ response['Content-Disposition'] = 'attachment; filename=users.csv'
+
+ writer = csv.writer(response)
+ writer.writerow(['ID',
+ 'Uniq',
+ 'Real Name',
+ 'Admin',
+ 'Affiliation',
+ 'State',
+ 'Quota (GiB)',
+ 'Updated',])
+ users = User.objects.order_by('id')
+ for u in users:
+ writer.writerow([u.id,
+ u.uniq.encode("utf-8"),
+ u.realname.encode("utf-8"),
+ u.is_admin,
+ u.affiliation.encode("utf-8"),
+ u.state.encode("utf-8"),
+ u.quota,
+ u.updated])
+
+ return response
+
+@requires_admin
+def users_create(request):
+ if request.method == 'GET':
+ return render_response('users_create.html')
+ if request.method == 'POST':
+ user = User()
+ user.uniq = request.POST.get('uniq')
+ user.realname = request.POST.get('realname')
+ user.is_admin = True if request.POST.get('admin') else False
+ user.affiliation = request.POST.get('affiliation')
+ user.quota = int(request.POST.get('quota') or 0) * (1024 ** 3) # In GiB
+ user.renew_token()
+ user.save()
+ return redirect(users_info, user.id)
+
+@requires_login
+def users_profile(request):
+ next = request.GET.get('next')
+ user = User.objects.get(uniq=request.user)
+ states = [x[0] for x in User.ACCOUNT_STATE]
+ return render_response('users_profile.html',
+ user=user,
+ states=states,
+ next=next)
+
+@requires_login
+def users_edit(request):
+ user = User.objects.get(uniq=request.user)
+ user.realname = request.POST.get('realname')
+ user.affiliation = request.POST.get('affiliation')
+ user.is_verified = True
+ user.save()
+ next = request.POST.get('next')
+ if next:
+ return redirect(next)
+
+ status = 'success'
+ message = _('Profile has been updated')
+ html = render_to_string('users_profile.html', {
+ 'user': user,
+ 'status': status,
+ 'message': message})
+ return HttpResponse(html)
+
BACKEND_BLOCK_MODULE = 'pithos.backends.lib.hashfiler'
BACKEND_BLOCK_PATH = os.path.join(PROJECT_PATH, 'data/')
-
# Bypass authentication for user administration.
BYPASS_ADMIN_AUTH = False
# 'django.middleware.csrf.CsrfViewMiddleware',
# 'django.contrib.auth.middleware.AuthenticationMiddleware',
# 'django.contrib.messages.middleware.MessageMiddleware',
+ 'pithos.middleware.URLEncodedHeadersMiddleware',
'pithos.middleware.LoggingConfigMiddleware',
'pithos.middleware.AuthMiddleware'
)
# 'django.contrib.admindocs',
'pithos.im',
'pithos.api',
- 'pithos.public',
- 'pithos.ui'
+ 'pithos.ui',
+ 'south'
)
# Set the expiration time of newly created auth tokens
4 : 0
}
-SERVICE_NAME = 'Pithos+'
+SERVICE_NAME = 'Pithos'
# Where users should login with their invitation code
-INVITATION_LOGIN_TARGET = 'http://%s/im/login/invitation' \
+INVITATION_LOGIN_TARGET = '%s/im/login/invitation' \
'?code=%d' \
'&next=%s'
# Where users should activate their local account
-ACTIVATION_LOGIN_TARGET = 'http://%s/im/local/activate/' \
+ACTIVATION_LOGIN_TARGET = '%s/im/local/activate/' \
'?auth=%s' \
'&next=%s'
# Where users should reset their local password
-PASSWORD_RESET_TARGET = 'http://%s/im/local/reset/' \
+PASSWORD_RESET_TARGET = '%s/im/local/reset/' \
'?username=%s' \
'&next=%s'
+# Force user profile verification
+FORCE_PROFILE_UPDATE = False
+
# The server is behind a proy (apache and gunicorn setup).
USE_X_FORWARDED_HOST = False
+# Set umask (needed for gunicorn setup).
+#os.umask(0077)
+
# Use to log to a file.
LOGFILE = None
+
+# Identity Management enabled modules
+IM_STANDARD_MODULES = ['local', 'invitation']
+IM_OTHER_MODULES = ['twitter', 'shibboleth']