1 # Copyright 2011 GRNET S.A. All rights reserved.
3 # Redistribution and use in source and binary forms, with or
4 # without modification, are permitted provided that the following
7 # 1. Redistributions of source code must retain the above
8 # copyright notice, this list of conditions and the following
11 # 2. Redistributions in binary form must reproduce the above
12 # copyright notice, this list of conditions and the following
13 # disclaimer in the documentation and/or other materials
14 # provided with the distribution.
16 # THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 # USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 # POSSIBILITY OF SUCH DAMAGE.
29 # The views and conclusions contained in the software and
30 # documentation are those of the authors and should not be
31 # interpreted as representing official policies, either expressed
32 # or implied, of GRNET S.A.
38 from datetime import datetime
39 from functools import wraps
41 from random import randint
42 from smtplib import SMTPException
44 from django.conf import settings
45 from django.core.mail import send_mail
46 from django.http import HttpResponse, HttpResponseRedirect
47 from django.shortcuts import redirect
48 from django.template.loader import render_to_string
49 from django.utils.http import urlencode
50 from django.utils.translation import ugettext as _
51 from django.core.urlresolvers import reverse
53 from pithos.im.models import User, Invitation
54 from pithos.im.util import isoformat
57 def render_response(template, tab=None, status=200, **kwargs):
59 tab = template.partition('_')[0]
60 kwargs.setdefault('tab', tab)
61 html = render_to_string(template, kwargs)
62 return HttpResponse(html, status=status)
65 def requires_login(func):
67 def wrapper(request, *args):
68 if not settings.BYPASS_ADMIN_AUTH:
70 next = urlencode({'next': request.build_absolute_uri()})
71 login_uri = reverse(index) + '?' + next
72 return HttpResponseRedirect(login_uri)
73 return func(request, *args)
77 def requires_admin(func):
79 def wrapper(request, *args):
80 if not settings.BYPASS_ADMIN_AUTH:
82 next = urlencode({'next': request.build_absolute_uri()})
83 login_uri = reverse(index) + '?' + next
84 return HttpResponseRedirect(login_uri)
85 if not request.user.is_admin:
86 return HttpResponse('Forbidden', status=403)
87 return func(request, *args)
92 return render_response('index.html', next=request.GET.get('next', ''))
98 stats['users'] = User.objects.count()
100 invitations = Invitation.objects.all()
101 stats['invitations'] = invitations.count()
102 stats['invitations_accepted'] = invitations.filter(is_accepted=True).count()
104 return render_response('admin.html', tab='home', stats=stats)
108 def users_list(request):
109 users = User.objects.order_by('id')
111 filter = request.GET.get('filter', '')
113 if filter.startswith('-'):
114 users = users.exclude(uniq__icontains=filter[1:])
116 users = users.filter(uniq__icontains=filter)
119 page = int(request.GET.get('page', 1))
122 offset = max(0, page - 1) * settings.ADMIN_PAGE_LIMIT
123 limit = offset + settings.ADMIN_PAGE_LIMIT
125 npages = int(ceil(1.0 * users.count() / settings.ADMIN_PAGE_LIMIT))
126 prev = page - 1 if page > 1 else None
127 next = page + 1 if page < npages else None
128 return render_response('users_list.html',
129 users=users[offset:limit],
131 pages=range(1, npages + 1),
138 def users_create(request):
139 if request.method == 'GET':
140 return render_response('users_create.html')
141 if request.method == 'POST':
143 user.uniq = request.POST.get('uniq')
144 user.realname = request.POST.get('realname')
145 user.is_admin = True if request.POST.get('admin') else False
146 user.affiliation = request.POST.get('affiliation')
147 user.quota = int(request.POST.get('quota') or 0) * (1024 ** 3) # In GiB
150 return redirect(users_info, user.id)
154 def users_info(request, user_id):
155 user = User.objects.get(id=user_id)
156 states = [x[0] for x in User.ACCOUNT_STATE]
157 return render_response('users_info.html',
163 def users_modify(request, user_id):
164 user = User.objects.get(id=user_id)
165 user.uniq = request.POST.get('uniq')
166 user.realname = request.POST.get('realname')
167 user.is_admin = True if request.POST.get('admin') else False
168 user.affiliation = request.POST.get('affiliation')
169 user.state = request.POST.get('state')
170 user.invitations = int(request.POST.get('invitations') or 0)
171 user.quota = int(request.POST.get('quota') or 0) * (1024 ** 3) # In GiB
172 user.auth_token = request.POST.get('auth_token')
174 auth_token_expires = request.POST.get('auth_token_expires')
175 d = datetime.strptime(auth_token_expires, '%Y-%m-%dT%H:%MZ')
176 user.auth_token_expires = d
180 return redirect(users_info, user.id)
184 def users_delete(request, user_id):
185 user = User.objects.get(id=user_id)
187 return redirect(users_list)
190 def generate_invitation_code():
192 code = randint(1, 2L**63 - 1)
194 Invitation.objects.get(code=code)
195 # An invitation with this code already exists, try again
196 except Invitation.DoesNotExist:
200 def send_invitation(inv):
201 url = settings.INVITATION_LOGIN_TARGET % inv.code
202 subject = _('Invitation to Pithos')
203 message = render_to_string('invitation.txt', {
206 sender = settings.DEFAULT_FROM_EMAIL
207 send_mail(subject, message, sender, [inv.uniq])
208 logging.info('Sent invitation %s', inv)
215 inviter = request.user
217 if request.method == 'POST':
218 uniq = request.POST.get('uniq')
219 realname = request.POST.get('realname')
221 if inviter.invitations > 0:
222 code = generate_invitation_code()
223 invitation, created = Invitation.objects.get_or_create(
226 defaults={'code': code, 'realname': realname})
229 send_invitation(invitation)
231 inviter.invitations = max(0, inviter.invitations - 1)
234 message = _('Invitation sent to %s' % uniq)
235 except (SMTPException, socket.error) as e:
237 message = getattr(e, 'strerror', '')
240 message = _('No invitations left')
242 if request.GET.get('format') == 'json':
243 sent = [{'email': inv.uniq,
244 'realname': inv.realname,
245 'is_accepted': inv.is_accepted}
246 for inv in inviter.invitations_sent.all()]
247 rep = {'invitations': inviter.invitations, 'sent': sent}
248 return HttpResponse(json.dumps(rep))
250 html = render_to_string('invitations.html', {
254 return HttpResponse(html)