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):
156 rd['comments'] = r.comments
157 rd['match'] = r.get_match()
158 rd['then'] = r.get_then()
159 rd['status'] = r.status
160 rd['applier'] = r.applier.username
162 rd['peer'] = r.applier.get_profile().peer.peer_name
163 except UserProfile.DoesNotExist:
165 rd['expires'] = "%s" %r.expires
166 rd['response'] = "%s" %r.response
172 def add_route(request):
173 applier = request.user.pk
174 applier_peer_networks = request.user.get_profile().peer.networks.all()
175 if request.user.is_superuser:
176 applier_peer_networks = PeerRange.objects.all()
177 if not applier_peer_networks:
178 messages.add_message(request, messages.WARNING,
179 _("Insufficient rights on administrative networks. Cannot add rule. Contact your administrator"))
180 return HttpResponseRedirect(reverse("group-routes"))
181 if request.method == "GET":
182 form = RouteForm(initial={'applier': applier})
183 if not request.user.is_superuser:
184 form.fields['then'] = forms.ModelMultipleChoiceField(queryset=ThenAction.objects.filter(action__in=settings.UI_USER_THEN_ACTIONS).order_by('action'), required=True)
185 form.fields['protocol'] = forms.ModelMultipleChoiceField(queryset=MatchProtocol.objects.filter(protocol__in=settings.UI_USER_PROTOCOLS).order_by('protocol'), required=False)
186 return render_to_response('apply.html', {'form': form, 'applier': applier},
187 context_instance=RequestContext(request))
190 request_data = request.POST.copy()
191 if request.user.is_superuser:
192 request_data['issuperuser'] = request.user.username
194 request_data['applier'] = applier
196 del requset_data['issuperuser']
199 form = RouteForm(request_data)
201 route=form.save(commit=False)
202 if not request.user.is_superuser:
203 route.applier = request.user
204 route.status = "PENDING"
205 route.response = "Applying"
206 route.source = IPNetwork("%s/%s" %(IPNetwork(route.source).network.compressed, IPNetwork(route.source).prefixlen)).compressed
207 route.destination = IPNetwork("%s/%s" %(IPNetwork(route.destination).network.compressed, IPNetwork(route.destination).prefixlen)).compressed
211 requesters_address = request.META['HTTP_X_FORWARDED_FOR']
212 fqdn = Site.objects.get_current().domain
213 admin_url = "https://%s%s" % (fqdn, reverse("edit-route", kwargs={'route_slug': route.name }))
214 mail_body = render_to_string("rule_action.txt",
215 {"route": route, "address": requesters_address, "action": "creation", "url": admin_url})
216 user_mail = "%s" %route.applier.email
217 user_mail = user_mail.split(';')
218 send_new_mail(settings.EMAIL_SUBJECT_PREFIX + "Rule %s creation request submitted by %s" %(route.name, route.applier.username),
219 mail_body, settings.SERVER_EMAIL, user_mail,
220 get_peer_techc_mails(route.applier))
221 d = { 'clientip' : "%s"%requesters_address, 'user' : route.applier.username }
222 logger.info(mail_body, extra=d)
223 return HttpResponseRedirect(reverse("group-routes"))
225 if not request.user.is_superuser:
226 form.fields['then'] = forms.ModelMultipleChoiceField(queryset=ThenAction.objects.filter(action__in=settings.UI_USER_THEN_ACTIONS).order_by('action'), required=True)
227 form.fields['protocol'] = forms.ModelMultipleChoiceField(queryset=MatchProtocol.objects.filter(protocol__in=settings.UI_USER_PROTOCOLS).order_by('protocol'), required=False)
228 return render_to_response('apply.html', {'form': form, 'applier':applier},
229 context_instance=RequestContext(request))
233 def edit_route(request, route_slug):
234 applier = request.user.pk
235 applier_peer = request.user.get_profile().peer
236 route_edit = get_object_or_404(Route, name=route_slug)
237 route_edit_applier_peer = route_edit.applier.get_profile().peer
238 if applier_peer != route_edit_applier_peer and (not request.user.is_superuser):
239 messages.add_message(request, messages.WARNING,
240 _("Insufficient rights to edit rule %s") %(route_slug))
241 return HttpResponseRedirect(reverse("group-routes"))
242 # if route_edit.status == "ADMININACTIVE" :
243 # messages.add_message(request, messages.WARNING,
244 # "Administrator has disabled editing of rule %s" %(route_slug))
245 # return HttpResponseRedirect(reverse("group-routes"))
246 # if route_edit.status == "EXPIRED" :
247 # messages.add_message(request, messages.WARNING,
248 # "Cannot edit the expired rule %s. Contact helpdesk to enable it" %(route_slug))
249 # return HttpResponseRedirect(reverse("group-routes"))
250 if route_edit.status == "PENDING" :
251 messages.add_message(request, messages.WARNING,
252 _("Cannot edit a pending rule: %s.") %(route_slug))
253 return HttpResponseRedirect(reverse("group-routes"))
254 route_original = deepcopy(route_edit)
256 request_data = request.POST.copy()
257 if request.user.is_superuser:
258 request_data['issuperuser'] = request.user.username
260 request_data['applier'] = applier
262 del request_data['issuperuser']
265 form = RouteForm(request_data, instance = route_edit)
266 critical_changed_values = ['source', 'destination', 'sourceport', 'destinationport', 'port', 'protocol', 'then', 'fragmenttype']
268 changed_data = form.changed_data
269 route=form.save(commit=False)
270 route.name = route_original.name
271 route.status = route_original.status
272 route.response = route_original.response
273 if not request.user.is_superuser:
274 route.applier = request.user
275 if bool(set(changed_data) & set(critical_changed_values)) or (not route_original.status == 'ACTIVE'):
276 route.status = "PENDING"
277 route.response = "Applying"
278 route.source = IPNetwork("%s/%s" %(IPNetwork(route.source).network.compressed, IPNetwork(route.source).prefixlen)).compressed
279 route.destination = IPNetwork("%s/%s" %(IPNetwork(route.destination).network.compressed, IPNetwork(route.destination).prefixlen)).compressed
281 if bool(set(changed_data) & set(critical_changed_values)) or (not route_original.status == 'ACTIVE'):
284 requesters_address = request.META['HTTP_X_FORWARDED_FOR']
285 fqdn = Site.objects.get_current().domain
286 admin_url = "https://%s%s" % (fqdn, reverse("edit-route", kwargs={'route_slug': route.name }))
287 mail_body = render_to_string("rule_action.txt",
288 {"route": route, "address": requesters_address, "action": "edit", "url": admin_url})
289 user_mail = "%s" %route.applier.email
290 user_mail = user_mail.split(';')
291 send_new_mail(settings.EMAIL_SUBJECT_PREFIX + "Rule %s edit request submitted by %s" %(route.name, route.applier.username),
292 mail_body, settings.SERVER_EMAIL, user_mail,
293 get_peer_techc_mails(route.applier))
294 d = { 'clientip' : requesters_address, 'user' : route.applier.username }
295 logger.info(mail_body, extra=d)
296 return HttpResponseRedirect(reverse("group-routes"))
298 if not request.user.is_superuser:
299 form.fields['then'] = forms.ModelMultipleChoiceField(queryset=ThenAction.objects.filter(action__in=settings.UI_USER_THEN_ACTIONS).order_by('action'), required=True)
300 form.fields['protocol'] = forms.ModelMultipleChoiceField(queryset=MatchProtocol.objects.filter(protocol__in=settings.UI_USER_PROTOCOLS).order_by('protocol'), required=False)
301 return render_to_response('apply.html', {'form': form, 'edit':True, 'applier': applier},
302 context_instance=RequestContext(request))
304 if (not route_original.status == 'ACTIVE'):
305 route_edit.expires = datetime.date.today() + datetime.timedelta(days = settings.EXPIRATION_DAYS_OFFSET)
306 dictionary = model_to_dict(route_edit, fields=[], exclude=[])
307 if request.user.is_superuser:
308 dictionary['issuperuser'] = request.user.username
311 del dictionary['issuperuser']
314 form = RouteForm(dictionary)
315 if not request.user.is_superuser:
316 form.fields['then'] = forms.ModelMultipleChoiceField(queryset=ThenAction.objects.filter(action__in=settings.UI_USER_THEN_ACTIONS).order_by('action'), required=True)
317 form.fields['protocol'] = forms.ModelMultipleChoiceField(queryset=MatchProtocol.objects.filter(protocol__in=settings.UI_USER_PROTOCOLS).order_by('protocol'), required=False)
318 return render_to_response('apply.html', {'form': form, 'edit':True, 'applier': applier},
319 context_instance=RequestContext(request))
323 def delete_route(request, route_slug):
324 if request.is_ajax():
325 route = get_object_or_404(Route, name=route_slug)
326 applier_peer = route.applier.get_profile().peer
327 requester_peer = request.user.get_profile().peer
328 if applier_peer == requester_peer or request.user.is_superuser:
329 route.status = "PENDING"
330 route.expires = datetime.date.today()
331 if not request.user.is_superuser:
332 route.applier = request.user
333 route.response = "Deactivating"
335 route.commit_delete()
336 requesters_address = request.META['HTTP_X_FORWARDED_FOR']
337 fqdn = Site.objects.get_current().domain
338 admin_url = "https://%s%s" % (fqdn, reverse("edit-route", kwargs={'route_slug': route.name }))
339 mail_body = render_to_string("rule_action.txt",
340 {"route": route, "address": requesters_address, "action": "removal", "url": admin_url})
341 user_mail = "%s" %route.applier.email
342 user_mail = user_mail.split(';')
343 send_new_mail(settings.EMAIL_SUBJECT_PREFIX + "Rule %s removal request submitted by %s" %(route.name, route.applier.username),
344 mail_body, settings.SERVER_EMAIL, user_mail,
345 get_peer_techc_mails(route.applier))
346 d = { 'clientip' : requesters_address, 'user' : route.applier.username }
347 logger.info(mail_body, extra=d)
348 html = "<html><body>Done</body></html>"
349 return HttpResponse(html)
351 return HttpResponseRedirect(reverse("group-routes"))
355 def user_profile(request):
358 peer = request.user.get_profile().peer
359 peers = Peer.objects.filter(pk=peer.pk)
360 if user.is_superuser:
361 peers = Peer.objects.all()
362 except UserProfile.DoesNotExist:
363 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
364 return render_to_response('error.html', {'error': error}, context_instance=RequestContext(request))
365 return render_to_response('profile.html', {'user': user, 'peers':peers},
366 context_instance=RequestContext(request))
369 def user_login(request):
371 error_username = False
372 error_orgname = False
373 error_entitlement = False
375 has_entitlement = False
377 username = lookupShibAttr(settings.SHIB_USERNAME, request.META)
379 error_username = True
380 firstname = lookupShibAttr(settings.SHIB_FIRSTNAME, request.META)
381 lastname = lookupShibAttr(settings.SHIB_LASTNAME, request.META)
382 mail = lookupShibAttr(settings.SHIB_MAIL, request.META)
383 entitlement = lookupShibAttr(settings.SHIB_ENTITLEMENT, request.META)
384 #organization = request.META['HTTP_SHIB_HOMEORGANIZATION']
386 if settings.SHIB_AUTH_ENTITLEMENT in entitlement.split(";"):
387 has_entitlement = True
388 if not has_entitlement:
389 error_entitlement = True
390 # if not organization:
391 # error_orgname = True
395 error = _("Your idP should release the HTTP_EPPN attribute towards this service<br>")
397 # error = error + _("Your idP should release the HTTP_SHIB_HOMEORGANIZATION attribute towards this service<br>")
398 if error_entitlement:
399 error = error + _("Your idP should release an appropriate HTTP_SHIB_EP_ENTITLEMENT attribute towards this service<br>")
401 error = error + _("Your idP should release the HTTP_SHIB_INETORGPERSON_MAIL attribute towards this service")
402 if error_username or error_orgname or error_entitlement or error_mail:
403 return render_to_response('error.html', {'error': error, "missing_attributes": True},
404 context_instance=RequestContext(request))
406 if settings.SHIB_SLUGIFY_USERNAME:
407 username = slugify(username)
408 user = User.objects.get(username__exact=username)
410 user.first_name = firstname
411 user.last_name = lastname
416 user = authenticate(username=username, firstname=firstname, lastname=lastname, mail=mail, authsource='shibboleth')
420 peer = user.get_profile().peer
421 # peer = Peer.objects.get(domain_name=organization)
422 # up = UserProfile.objects.get_or_create(user=user,peer=peer)
424 form = UserProfileForm()
425 form.fields['user'] = forms.ModelChoiceField(queryset=User.objects.filter(pk=user.pk), empty_label=None)
426 form.fields['peer'] = forms.ModelChoiceField(queryset=Peer.objects.all(), empty_label=None)
427 return render_to_response('registration/select_institution.html', {'form': form}, context_instance=RequestContext(request))
429 user_activation_notify(user)
432 return HttpResponseRedirect(reverse("dashboard"))
434 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
435 return render_to_response('error.html', {'error': error, 'inactive': True},
436 context_instance=RequestContext(request))
438 error = _("Something went wrong during user authentication. Contact your administrator")
439 return render_to_response('error.html', {'error': error,},
440 context_instance=RequestContext(request))
441 except User.DoesNotExist as e:
442 error = _("Invalid login procedure. Error: %s" %e)
443 return render_to_response('error.html', {'error': error,},
444 context_instance=RequestContext(request))
445 # Return an 'invalid login' error message.
446 # return HttpResponseRedirect(reverse("user-routes"))
448 def user_activation_notify(user):
449 current_site = Site.objects.get_current()
450 peer = user.get_profile().peer
453 # Email subject *must not* contain newlines
454 # TechCs will be notified about new users.
455 # Platform admins will activate the users.
456 subject = render_to_string('registration/activation_email_subject.txt',
457 { 'site': current_site })
458 subject = ''.join(subject.splitlines())
459 registration_profile = RegistrationProfile.objects.create_profile(user)
460 message = render_to_string('registration/activation_email.txt',
461 { 'activation_key': registration_profile.activation_key,
462 'expiration_days': settings.ACCOUNT_ACTIVATION_DAYS,
463 'site': current_site,
465 if settings.NOTIFY_ADMIN_MAILS:
466 admin_mails = settings.NOTIFY_ADMIN_MAILS
467 send_new_mail(settings.EMAIL_SUBJECT_PREFIX + subject,
468 message, settings.SERVER_EMAIL,
471 # Mail to domain techCs plus platform admins (no activation hash sent)
472 subject = render_to_string('registration/activation_email_peer_notify_subject.txt',
473 { 'site': current_site,
475 subject = ''.join(subject.splitlines())
476 message = render_to_string('registration/activation_email_peer_notify.txt',
479 send_new_mail(settings.EMAIL_SUBJECT_PREFIX + subject,
480 message, settings.SERVER_EMAIL,
481 get_peer_techc_mails(user), [])
485 def add_rate_limit(request):
486 if request.method == "GET":
487 form = ThenPlainForm()
488 return render_to_response('add_rate_limit.html', {'form': form,},
489 context_instance=RequestContext(request))
492 form = ThenPlainForm(request.POST)
494 then=form.save(commit=False)
495 then.action_value = "%sk"%then.action_value
498 response_data['pk'] = "%s" %then.pk
499 response_data['value'] = "%s:%s" %(then.action, then.action_value)
500 return HttpResponse(json.dumps(response_data), mimetype='application/json')
502 return render_to_response('add_rate_limit.html', {'form': form,},
503 context_instance=RequestContext(request))
507 def add_port(request):
508 if request.method == "GET":
509 form = PortPlainForm()
510 return render_to_response('add_port.html', {'form': form,},
511 context_instance=RequestContext(request))
514 form = PortPlainForm(request.POST)
518 response_data['value'] = "%s" %port.pk
519 response_data['text'] = "%s" %port.port
520 return HttpResponse(json.dumps(response_data), mimetype='application/json')
522 return render_to_response('add_port.html', {'form': form,},
523 context_instance=RequestContext(request))
526 def selectinst(request):
527 if request.method == 'POST':
528 request_data = request.POST.copy()
529 user = request_data['user']
531 existingProfile = UserProfile.objects.get(user=user)
532 error = _("Violation warning: User account is already associated with an institution.The event has been logged and our administrators will be notified about it")
533 return render_to_response('error.html', {'error': error, 'inactive': True},
534 context_instance=RequestContext(request))
535 except UserProfile.DoesNotExist:
538 form = UserProfileForm(request_data)
540 userprofile = form.save()
541 user_activation_notify(userprofile.user)
542 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
543 return render_to_response('error.html', {'error': error, 'inactive': True},
544 context_instance=RequestContext(request))
546 form.fields['user'] = forms.ModelChoiceField(queryset=User.objects.filter(pk=user.pk), empty_label=None)
547 form.fields['institution'] = forms.ModelChoiceField(queryset=Peer.objects.all(), empty_label=None)
548 return render_to_response('registration/select_institution.html', {'form': form}, context_instance=RequestContext(request))
551 def overview(request):
553 if user.is_authenticated():
554 if user.has_perm('accounts.overview'):
555 users = User.objects.all()
556 return render_to_response('overview/index.html', {'users': users},
557 context_instance=RequestContext(request))
560 return render_to_response('overview/index.html', {'violation': violation},
561 context_instance=RequestContext(request))
563 return HttpResponseRedirect(reverse("altlogin"))
567 def user_logout(request):
569 return HttpResponseRedirect(reverse('group-routes'))
572 def load_jscript(request, file):
573 long_polling_timeout = int(settings.POLL_SESSION_UPDATE)*1000 + 10000
574 return render_to_response('%s.js' % file, {'timeout': long_polling_timeout}, context_instance=RequestContext(request), mimetype="text/javascript")
577 def get_peer_techc_mails(user):
581 user_mail = "%s" %user.email
582 user_mail = user_mail.split(';')
583 techmails = user.get_profile().peer.techc_emails.all()
585 for techmail in techmails:
586 techmails_list.append(techmail.email)
587 if settings.NOTIFY_ADMIN_MAILS:
588 additional_mail = settings.NOTIFY_ADMIN_MAILS
589 mail.extend(additional_mail)
590 mail.extend(techmails_list)
593 def send_new_mail(subject, message, from_email, recipient_list, bcc_list):
594 return EmailMessage(subject, message, from_email, recipient_list, bcc_list).send()
597 def lookupShibAttr(attrmap, requestMeta):
599 if (attr in requestMeta.keys()):
600 if len(requestMeta[attr]) > 0:
601 return requestMeta[attr]