1 # -*- coding: utf-8 -*- vim:fileencoding=utf-8:
2 # vim: tabstop=4:shiftwidth=4:softtabstop=4:expandtab
4 # Copyright (C) 2010-2014 GRNET S.A.
6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation, either version 3 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
23 from django import forms
24 from django.views.decorators.csrf import csrf_exempt
25 from django.core import urlresolvers
26 from django.core import serializers
27 from django.contrib.auth.decorators import login_required
28 from django.contrib.auth import logout
29 from django.contrib.sites.models import Site
30 from django.contrib.auth.models import User
31 from django.http import HttpResponseRedirect, HttpResponseForbidden, HttpResponse
32 from django.shortcuts import get_object_or_404, render_to_response
33 from django.core.context_processors import request
34 from django.template.context import RequestContext
35 from django.template.loader import get_template, render_to_string
36 from django.utils.translation import ugettext as _
37 from django.core.urlresolvers import reverse
38 from django.contrib import messages
39 from accounts.models import *
42 from django.contrib.auth import authenticate, login
44 from django.forms.models import model_to_dict
46 from flowspec.forms import *
47 from flowspec.models import *
48 from peers.models import *
50 from registration.models import RegistrationProfile
52 from copy import deepcopy
53 from utils.decorators import shib_required
55 from django.views.decorators.cache import never_cache
56 from django.conf import settings
57 from django.core.mail.message import EmailMessage
58 from django.template.defaultfilters import slugify
62 LOG_FILENAME = os.path.join(settings.LOG_FILE_LOCATION, 'celery_jobs.log')
63 #FORMAT = '%(asctime)s %(levelname)s: %(message)s'
64 #logging.basicConfig(format=FORMAT)
65 formatter = logging.Formatter('%(asctime)s %(levelname)s %(clientip)s %(user)s: %(message)s')
67 logger = logging.getLogger(__name__)
68 logger.setLevel(logging.DEBUG)
69 handler = logging.FileHandler(LOG_FILENAME)
70 handler.setFormatter(formatter)
71 logger.addHandler(handler)
74 def user_routes(request):
75 user_routes = Route.objects.filter(applier=request.user)
76 return render_to_response('user_routes.html', {'routes': user_routes},
77 context_instance=RequestContext(request))
80 return render_to_response('welcome.html', context_instance=RequestContext(request))
84 def dashboard(request):
87 peer = request.user.get_profile().peer
88 except UserProfile.DoesNotExist:
89 error = "User <strong>%s</strong> does not belong to any peer or organization. It is not possible to create new firewall rules.<br>Please contact Helpdesk to resolve this issue" % request.user.username
90 return render_to_response('error.html', {'error': error}, context_instance=RequestContext(request))
92 peer_members = UserProfile.objects.filter(peer=peer)
93 users = [prof.user for prof in peer_members]
94 group_routes = Route.objects.filter(applier__in=users).order_by('-expires')[:10]
95 if request.user.is_superuser:
96 group_routes = Route.objects.all().order_by('-expires')[:10]
97 return render_to_response('dashboard.html', {'routes': group_routes},
98 context_instance=RequestContext(request))
102 def group_routes(request):
104 peer = request.user.get_profile().peer
105 except UserProfile.DoesNotExist:
106 error = "User <strong>%s</strong> does not belong to any peer or organization. It is not possible to create new firewall rules.<br>Please contact Helpdesk to resolve this issue" % request.user.username
107 return render_to_response('error.html', {'error': error}, context_instance=RequestContext(request))
108 return render_to_response('user_routes.html', context_instance=RequestContext(request))
112 def group_routes_ajax(request):
115 peer = request.user.get_profile().peer
116 except UserProfile.DoesNotExist:
117 error = "User <strong>%s</strong> does not belong to any peer or organization. It is not possible to create new firewall rules.<br>Please contact Helpdesk to resolve this issue" % request.user.username
118 return render_to_response('error.html', {'error': error}, context_instance=RequestContext(request))
120 peer_members = UserProfile.objects.filter(peer=peer)
121 users = [prof.user for prof in peer_members]
122 group_routes = Route.objects.filter(applier__in=users)
123 if request.user.is_superuser:
124 group_routes = Route.objects.all()
126 routes = build_routes_json(group_routes)
127 jresp['aaData'] = routes
128 return HttpResponse(json.dumps(jresp), mimetype='application/json')
132 def overview_routes_ajax(request):
135 peer = request.user.get_profile().peer
136 except UserProfile.DoesNotExist:
137 error = "User <strong>%s</strong> does not belong to any peer or organization. It is not possible to create new firewall rules.<br>Please contact Helpdesk to resolve this issue" % request.user.username
138 return render_to_response('error.html', {'error': error}, context_instance=RequestContext(request))
140 peer_members = UserProfile.objects.filter(peer=peer)
141 users = [prof.user for prof in peer_members]
142 group_routes = Route.objects.filter(applier__in=users)
143 if request.user.is_superuser or request.user.has_perm('accounts.overview'):
144 group_routes = Route.objects.all()
146 routes = build_routes_json(group_routes)
147 jresp['aaData'] = routes
148 return HttpResponse(json.dumps(jresp), mimetype='application/json')
150 def build_routes_json(groutes):
157 rd['comments'] = 'Not Any'
159 rd['comments'] = r.comments
160 rd['match'] = r.get_match()
161 rd['then'] = r.get_then()
162 rd['status'] = r.status
163 rd['applier'] = r.applier.username
165 rd['peer'] = r.applier.get_profile().peer.peer_name
166 except UserProfile.DoesNotExist:
168 rd['expires'] = "%s" %r.expires
169 rd['response'] = "%s" %r.response
175 def add_route(request):
176 applier = request.user.pk
177 applier_peer_networks = request.user.get_profile().peer.networks.all()
178 if request.user.is_superuser:
179 applier_peer_networks = PeerRange.objects.all()
180 if not applier_peer_networks:
181 messages.add_message(request, messages.WARNING,
182 _("Insufficient rights on administrative networks. Cannot add rule. Contact your administrator"))
183 return HttpResponseRedirect(reverse("group-routes"))
184 if request.method == "GET":
185 form = RouteForm(initial={'applier': applier})
186 if not request.user.is_superuser:
187 form.fields['then'] = forms.ModelMultipleChoiceField(queryset=ThenAction.objects.filter(action__in=settings.UI_USER_THEN_ACTIONS).order_by('action'), required=True)
188 form.fields['protocol'] = forms.ModelMultipleChoiceField(queryset=MatchProtocol.objects.filter(protocol__in=settings.UI_USER_PROTOCOLS).order_by('protocol'), required=False)
189 return render_to_response('apply.html', {'form': form, 'applier': applier},
190 context_instance=RequestContext(request))
193 request_data = request.POST.copy()
194 if request.user.is_superuser:
195 request_data['issuperuser'] = request.user.username
197 request_data['applier'] = applier
199 del requset_data['issuperuser']
202 form = RouteForm(request_data)
204 route=form.save(commit=False)
205 if not request.user.is_superuser:
206 route.applier = request.user
207 route.status = "PENDING"
208 route.response = "Applying"
209 route.source = IPNetwork("%s/%s" %(IPNetwork(route.source).network.compressed, IPNetwork(route.source).prefixlen)).compressed
210 route.destination = IPNetwork("%s/%s" %(IPNetwork(route.destination).network.compressed, IPNetwork(route.destination).prefixlen)).compressed
214 requesters_address = request.META['HTTP_X_FORWARDED_FOR']
215 fqdn = Site.objects.get_current().domain
216 admin_url = "https://%s%s" % (fqdn, reverse("edit-route", kwargs={'route_slug': route.name }))
217 mail_body = render_to_string("rule_action.txt",
218 {"route": route, "address": requesters_address, "action": "creation", "url": admin_url})
219 user_mail = "%s" %route.applier.email
220 user_mail = user_mail.split(';')
221 send_new_mail(settings.EMAIL_SUBJECT_PREFIX + "Rule %s creation request submitted by %s" %(route.name, route.applier.username),
222 mail_body, settings.SERVER_EMAIL, user_mail,
223 get_peer_techc_mails(route.applier))
224 d = { 'clientip' : "%s"%requesters_address, 'user' : route.applier.username }
225 logger.info(mail_body, extra=d)
226 return HttpResponseRedirect(reverse("group-routes"))
228 if not request.user.is_superuser:
229 form.fields['then'] = forms.ModelMultipleChoiceField(queryset=ThenAction.objects.filter(action__in=settings.UI_USER_THEN_ACTIONS).order_by('action'), required=True)
230 form.fields['protocol'] = forms.ModelMultipleChoiceField(queryset=MatchProtocol.objects.filter(protocol__in=settings.UI_USER_PROTOCOLS).order_by('protocol'), required=False)
231 return render_to_response('apply.html', {'form': form, 'applier':applier},
232 context_instance=RequestContext(request))
236 def edit_route(request, route_slug):
237 applier = request.user.pk
238 applier_peer = request.user.get_profile().peer
239 route_edit = get_object_or_404(Route, name=route_slug)
240 route_edit_applier_peer = route_edit.applier.get_profile().peer
241 if applier_peer != route_edit_applier_peer and (not request.user.is_superuser):
242 messages.add_message(request, messages.WARNING,
243 _("Insufficient rights to edit rule %s") %(route_slug))
244 return HttpResponseRedirect(reverse("group-routes"))
245 # if route_edit.status == "ADMININACTIVE" :
246 # messages.add_message(request, messages.WARNING,
247 # "Administrator has disabled editing of rule %s" %(route_slug))
248 # return HttpResponseRedirect(reverse("group-routes"))
249 # if route_edit.status == "EXPIRED" :
250 # messages.add_message(request, messages.WARNING,
251 # "Cannot edit the expired rule %s. Contact helpdesk to enable it" %(route_slug))
252 # return HttpResponseRedirect(reverse("group-routes"))
253 if route_edit.status == "PENDING" :
254 messages.add_message(request, messages.WARNING,
255 _("Cannot edit a pending rule: %s.") %(route_slug))
256 return HttpResponseRedirect(reverse("group-routes"))
257 route_original = deepcopy(route_edit)
259 request_data = request.POST.copy()
260 if request.user.is_superuser:
261 request_data['issuperuser'] = request.user.username
263 request_data['applier'] = applier
265 del request_data['issuperuser']
268 form = RouteForm(request_data, instance = route_edit)
269 critical_changed_values = ['source', 'destination', 'sourceport', 'destinationport', 'port', 'protocol', 'then', 'fragmenttype']
271 changed_data = form.changed_data
272 route=form.save(commit=False)
273 route.name = route_original.name
274 route.status = route_original.status
275 route.response = route_original.response
276 if not request.user.is_superuser:
277 route.applier = request.user
278 if bool(set(changed_data) & set(critical_changed_values)) or (not route_original.status == 'ACTIVE'):
279 route.status = "PENDING"
280 route.response = "Applying"
281 route.source = IPNetwork("%s/%s" %(IPNetwork(route.source).network.compressed, IPNetwork(route.source).prefixlen)).compressed
282 route.destination = IPNetwork("%s/%s" %(IPNetwork(route.destination).network.compressed, IPNetwork(route.destination).prefixlen)).compressed
284 if bool(set(changed_data) & set(critical_changed_values)) or (not route_original.status == 'ACTIVE'):
287 requesters_address = request.META['HTTP_X_FORWARDED_FOR']
288 fqdn = Site.objects.get_current().domain
289 admin_url = "https://%s%s" % (fqdn, reverse("edit-route", kwargs={'route_slug': route.name }))
290 mail_body = render_to_string("rule_action.txt",
291 {"route": route, "address": requesters_address, "action": "edit", "url": admin_url})
292 user_mail = "%s" %route.applier.email
293 user_mail = user_mail.split(';')
294 send_new_mail(settings.EMAIL_SUBJECT_PREFIX + "Rule %s edit request submitted by %s" %(route.name, route.applier.username),
295 mail_body, settings.SERVER_EMAIL, user_mail,
296 get_peer_techc_mails(route.applier))
297 d = { 'clientip' : requesters_address, 'user' : route.applier.username }
298 logger.info(mail_body, extra=d)
299 return HttpResponseRedirect(reverse("group-routes"))
301 if not request.user.is_superuser:
302 form.fields['then'] = forms.ModelMultipleChoiceField(queryset=ThenAction.objects.filter(action__in=settings.UI_USER_THEN_ACTIONS).order_by('action'), required=True)
303 form.fields['protocol'] = forms.ModelMultipleChoiceField(queryset=MatchProtocol.objects.filter(protocol__in=settings.UI_USER_PROTOCOLS).order_by('protocol'), required=False)
304 return render_to_response('apply.html', {'form': form, 'edit':True, 'applier': applier},
305 context_instance=RequestContext(request))
307 if (not route_original.status == 'ACTIVE'):
308 route_edit.expires = datetime.date.today() + datetime.timedelta(days = settings.EXPIRATION_DAYS_OFFSET)
309 dictionary = model_to_dict(route_edit, fields=[], exclude=[])
310 if request.user.is_superuser:
311 dictionary['issuperuser'] = request.user.username
314 del dictionary['issuperuser']
317 form = RouteForm(dictionary)
318 if not request.user.is_superuser:
319 form.fields['then'] = forms.ModelMultipleChoiceField(queryset=ThenAction.objects.filter(action__in=settings.UI_USER_THEN_ACTIONS).order_by('action'), required=True)
320 form.fields['protocol'] = forms.ModelMultipleChoiceField(queryset=MatchProtocol.objects.filter(protocol__in=settings.UI_USER_PROTOCOLS).order_by('protocol'), required=False)
321 return render_to_response('apply.html', {'form': form, 'edit':True, 'applier': applier},
322 context_instance=RequestContext(request))
326 def delete_route(request, route_slug):
327 if request.is_ajax():
328 route = get_object_or_404(Route, name=route_slug)
329 applier_peer = route.applier.get_profile().peer
330 requester_peer = request.user.get_profile().peer
331 if applier_peer == requester_peer or request.user.is_superuser:
332 route.status = "PENDING"
333 route.expires = datetime.date.today()
334 if not request.user.is_superuser:
335 route.applier = request.user
336 route.response = "Deactivating"
338 route.commit_delete()
339 requesters_address = request.META['HTTP_X_FORWARDED_FOR']
340 fqdn = Site.objects.get_current().domain
341 admin_url = "https://%s%s" % (fqdn, reverse("edit-route", kwargs={'route_slug': route.name }))
342 mail_body = render_to_string("rule_action.txt",
343 {"route": route, "address": requesters_address, "action": "removal", "url": admin_url})
344 user_mail = "%s" %route.applier.email
345 user_mail = user_mail.split(';')
346 send_new_mail(settings.EMAIL_SUBJECT_PREFIX + "Rule %s removal request submitted by %s" %(route.name, route.applier.username),
347 mail_body, settings.SERVER_EMAIL, user_mail,
348 get_peer_techc_mails(route.applier))
349 d = { 'clientip' : requesters_address, 'user' : route.applier.username }
350 logger.info(mail_body, extra=d)
351 html = "<html><body>Done</body></html>"
352 return HttpResponse(html)
354 return HttpResponseRedirect(reverse("group-routes"))
358 def user_profile(request):
361 peer = request.user.get_profile().peer
362 peers = Peer.objects.filter(pk=peer.pk)
363 if user.is_superuser:
364 peers = Peer.objects.all()
365 except UserProfile.DoesNotExist:
366 error = "User <strong>%s</strong> does not belong to any peer or organization. It is not possible to create new firewall rules.<br>Please contact Helpdesk to resolve this issue" % user.username
367 return render_to_response('error.html', {'error': error}, context_instance=RequestContext(request))
368 return render_to_response('profile.html', {'user': user, 'peers':peers},
369 context_instance=RequestContext(request))
372 def user_login(request):
374 error_username = False
375 error_orgname = False
376 error_entitlement = False
378 has_entitlement = False
380 username = lookupShibAttr(settings.SHIB_USERNAME, request.META)
382 error_username = True
383 firstname = lookupShibAttr(settings.SHIB_FIRSTNAME, request.META)
384 lastname = lookupShibAttr(settings.SHIB_LASTNAME, request.META)
385 mail = lookupShibAttr(settings.SHIB_MAIL, request.META)
386 entitlement = lookupShibAttr(settings.SHIB_ENTITLEMENT, request.META)
387 #organization = request.META['HTTP_SHIB_HOMEORGANIZATION']
389 if settings.SHIB_AUTH_ENTITLEMENT in entitlement.split(";"):
390 has_entitlement = True
391 if not has_entitlement:
392 error_entitlement = True
393 # if not organization:
394 # error_orgname = True
398 error = _("Your idP should release the HTTP_EPPN attribute towards this service<br>")
400 # error = error + _("Your idP should release the HTTP_SHIB_HOMEORGANIZATION attribute towards this service<br>")
401 if error_entitlement:
402 error = error + _("Your idP should release an appropriate HTTP_SHIB_EP_ENTITLEMENT attribute towards this service<br>")
404 error = error + _("Your idP should release the HTTP_SHIB_INETORGPERSON_MAIL attribute towards this service")
405 if error_username or error_orgname or error_entitlement or error_mail:
406 return render_to_response('error.html', {'error': error, "missing_attributes": True},
407 context_instance=RequestContext(request))
409 if settings.SHIB_SLUGIFY_USERNAME:
410 username = slugify(username)
411 user = User.objects.get(username__exact=username)
413 user.first_name = firstname
414 user.last_name = lastname
419 user = authenticate(username=username, firstname=firstname, lastname=lastname, mail=mail, authsource='shibboleth')
423 peer = user.get_profile().peer
424 # peer = Peer.objects.get(domain_name=organization)
425 # up = UserProfile.objects.get_or_create(user=user,peer=peer)
427 form = UserProfileForm()
428 form.fields['user'] = forms.ModelChoiceField(queryset=User.objects.filter(pk=user.pk), empty_label=None)
429 form.fields['peer'] = forms.ModelChoiceField(queryset=Peer.objects.all(), empty_label=None)
430 return render_to_response('registration/select_institution.html', {'form': form}, context_instance=RequestContext(request))
432 user_activation_notify(user)
435 return HttpResponseRedirect(reverse("dashboard"))
437 error = _("User account <strong>%s</strong> is pending activation. Administrators have been notified and will activate this account within the next days. <br>If this account has remained inactive for a long time contact your technical coordinator or GRNET Helpdesk") %user.username
438 return render_to_response('error.html', {'error': error, 'inactive': True},
439 context_instance=RequestContext(request))
441 error = _("Something went wrong during user authentication. Contact your administrator")
442 return render_to_response('error.html', {'error': error,},
443 context_instance=RequestContext(request))
444 except User.DoesNotExist as e:
445 error = _("Invalid login procedure. Error: %s" %e)
446 return render_to_response('error.html', {'error': error,},
447 context_instance=RequestContext(request))
448 # Return an 'invalid login' error message.
449 # return HttpResponseRedirect(reverse("user-routes"))
451 def user_activation_notify(user):
452 current_site = Site.objects.get_current()
453 peer = user.get_profile().peer
456 # Email subject *must not* contain newlines
457 # TechCs will be notified about new users.
458 # Platform admins will activate the users.
459 subject = render_to_string('registration/activation_email_subject.txt',
460 { 'site': current_site })
461 subject = ''.join(subject.splitlines())
462 registration_profile = RegistrationProfile.objects.create_profile(user)
463 message = render_to_string('registration/activation_email.txt',
464 { 'activation_key': registration_profile.activation_key,
465 'expiration_days': settings.ACCOUNT_ACTIVATION_DAYS,
466 'site': current_site,
468 if settings.NOTIFY_ADMIN_MAILS:
469 admin_mails = settings.NOTIFY_ADMIN_MAILS
470 send_new_mail(settings.EMAIL_SUBJECT_PREFIX + subject,
471 message, settings.SERVER_EMAIL,
474 # Mail to domain techCs plus platform admins (no activation hash sent)
475 subject = render_to_string('registration/activation_email_peer_notify_subject.txt',
476 { 'site': current_site,
478 subject = ''.join(subject.splitlines())
479 message = render_to_string('registration/activation_email_peer_notify.txt',
482 send_new_mail(settings.EMAIL_SUBJECT_PREFIX + subject,
483 message, settings.SERVER_EMAIL,
484 get_peer_techc_mails(user), [])
488 def add_rate_limit(request):
489 if request.method == "GET":
490 form = ThenPlainForm()
491 return render_to_response('add_rate_limit.html', {'form': form,},
492 context_instance=RequestContext(request))
495 form = ThenPlainForm(request.POST)
497 then=form.save(commit=False)
498 then.action_value = "%sk"%then.action_value
501 response_data['pk'] = "%s" %then.pk
502 response_data['value'] = "%s:%s" %(then.action, then.action_value)
503 return HttpResponse(json.dumps(response_data), mimetype='application/json')
505 return render_to_response('add_rate_limit.html', {'form': form,},
506 context_instance=RequestContext(request))
510 def add_port(request):
511 if request.method == "GET":
512 form = PortPlainForm()
513 return render_to_response('add_port.html', {'form': form,},
514 context_instance=RequestContext(request))
517 form = PortPlainForm(request.POST)
521 response_data['value'] = "%s" %port.pk
522 response_data['text'] = "%s" %port.port
523 return HttpResponse(json.dumps(response_data), mimetype='application/json')
525 return render_to_response('add_port.html', {'form': form,},
526 context_instance=RequestContext(request))
529 def selectinst(request):
530 if request.method == 'POST':
531 request_data = request.POST.copy()
532 user = request_data['user']
534 existingProfile = UserProfile.objects.get(user=user)
535 error = _("Violation warning: User account is already associated with an institution.The event has been logged and our administrators will be notified about it")
536 return render_to_response('error.html', {'error': error, 'inactive': True},
537 context_instance=RequestContext(request))
538 except UserProfile.DoesNotExist:
541 form = UserProfileForm(request_data)
543 userprofile = form.save()
544 user_activation_notify(userprofile.user)
545 error = _("User account <strong>%s</strong> is pending activation. Administrators have been notified and will activate this account within the next days. <br>If this account has remained inactive for a long time contact your technical coordinator or GRNET Helpdesk") %userprofile.user.username
546 return render_to_response('error.html', {'error': error, 'inactive': True},
547 context_instance=RequestContext(request))
549 form.fields['user'] = forms.ModelChoiceField(queryset=User.objects.filter(pk=user.pk), empty_label=None)
550 form.fields['institution'] = forms.ModelChoiceField(queryset=Peer.objects.all(), empty_label=None)
551 return render_to_response('registration/select_institution.html', {'form': form}, context_instance=RequestContext(request))
554 def overview(request):
556 if user.is_authenticated():
557 if user.has_perm('accounts.overview'):
558 users = User.objects.all()
559 return render_to_response('overview/index.html', {'users': users},
560 context_instance=RequestContext(request))
563 return render_to_response('overview/index.html', {'violation': violation},
564 context_instance=RequestContext(request))
566 return HttpResponseRedirect(reverse("altlogin"))
570 def user_logout(request):
572 return HttpResponseRedirect(reverse('group-routes'))
575 def load_jscript(request, file):
576 long_polling_timeout = int(settings.POLL_SESSION_UPDATE)*1000 + 10000
577 return render_to_response('%s.js' % file, {'timeout': long_polling_timeout}, context_instance=RequestContext(request), mimetype="text/javascript")
580 def get_peer_techc_mails(user):
584 user_mail = "%s" %user.email
585 user_mail = user_mail.split(';')
586 techmails = user.get_profile().peer.techc_emails.all()
588 for techmail in techmails:
589 techmails_list.append(techmail.email)
590 if settings.NOTIFY_ADMIN_MAILS:
591 additional_mail = settings.NOTIFY_ADMIN_MAILS
592 mail.extend(additional_mail)
593 mail.extend(techmails_list)
596 def send_new_mail(subject, message, from_email, recipient_list, bcc_list):
597 return EmailMessage(subject, message, from_email, recipient_list, bcc_list).send()
600 def lookupShibAttr(attrmap, requestMeta):
602 if (attr in requestMeta.keys()):
603 if len(requestMeta[attr]) > 0:
604 return requestMeta[attr]