2 # -*- coding: utf-8 -*- vim:fileencoding=utf-8:
3 #Copyright © 2011-2013 Greek Research and Technology Network (GRNET S.A.)
5 #Developed by Leonidas Poulopoulos (leopoul-at-noc-dot-grnet-dot-gr),
8 #Permission to use, copy, modify, and/or distribute this software for any
9 #purpose with or without fee is hereby granted, provided that the above
10 #copyright notice and this permission notice appear in all copies.
12 #THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
13 #TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
14 #FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
15 #CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
16 #DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
17 #ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
20 # Create your views here.
24 from django import forms
25 from django.views.decorators.csrf import csrf_exempt
26 from django.core import urlresolvers
27 from django.core import serializers
28 from django.contrib.auth.decorators import login_required
29 from django.contrib.auth import logout
30 from django.contrib.sites.models import Site
31 from django.contrib.auth.models import User
32 from django.http import HttpResponseRedirect, HttpResponseForbidden, HttpResponse
33 from django.shortcuts import get_object_or_404, render_to_response
34 from django.core.context_processors import request
35 from django.template.context import RequestContext
36 from django.template.loader import get_template, render_to_string
37 from django.utils.translation import ugettext as _
38 from django.core.urlresolvers import reverse
39 from django.contrib import messages
40 from flowspy.accounts.models import *
43 from django.contrib.auth import authenticate, login
45 from django.forms.models import model_to_dict
47 from flowspy.flowspec.forms import *
48 from flowspy.flowspec.models import *
49 from flowspy.peers.models import *
51 from registration.models import RegistrationProfile
53 from copy import deepcopy
54 from flowspy.utils.decorators import shib_required
56 from django.views.decorators.cache import never_cache
57 from django.conf import settings
58 from django.core.mail.message import EmailMessage
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 group_routes(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)
95 if request.user.is_superuser:
96 group_routes = Route.objects.all()
97 return render_to_response('user_routes.html', {'routes': group_routes},
98 context_instance=RequestContext(request))
103 def add_route(request):
104 applier = request.user.pk
105 applier_peer_networks = request.user.get_profile().peer.networks.all()
106 if not applier_peer_networks:
107 messages.add_message(request, messages.WARNING,
108 _("Insufficient rights on administrative networks. Cannot add rule. Contact your administrator"))
109 return HttpResponseRedirect(reverse("group-routes"))
110 if request.method == "GET":
111 form = RouteForm(initial={'applier': applier})
112 if not request.user.is_superuser:
113 form.fields['then'] = forms.ModelMultipleChoiceField(queryset=ThenAction.objects.filter(action__in=settings.UI_USER_THEN_ACTIONS).order_by('action'), required=True)
114 form.fields['protocol'] = forms.ModelMultipleChoiceField(queryset=MatchProtocol.objects.filter(protocol__in=settings.UI_USER_PROTOCOLS).order_by('protocol'), required=False)
115 return render_to_response('apply.html', {'form': form, 'applier': applier},
116 context_instance=RequestContext(request))
119 request_data = request.POST.copy()
120 if request.user.is_superuser:
121 request_data['issuperuser'] = request.user.username
123 request_data['applier'] = applier
125 del requset_data['issuperuser']
128 form = RouteForm(request_data)
130 route=form.save(commit=False)
131 if not request.user.is_superuser:
132 route.applier = request.user
133 route.status = "PENDING"
134 route.response = "Applying"
135 route.source = IPNetwork("%s/%s" %(IPNetwork(route.source).network.compressed, IPNetwork(route.source).prefixlen)).compressed
136 route.destination = IPNetwork("%s/%s" %(IPNetwork(route.destination).network.compressed, IPNetwork(route.destination).prefixlen)).compressed
140 requesters_address = request.META['HTTP_X_FORWARDED_FOR']
141 fqdn = Site.objects.get_current().domain
142 admin_url = "https://%s%s" % (fqdn, "/fod/edit/%s"%route.name)
143 mail_body = render_to_string("rule_action.txt",
144 {"route": route, "address": requesters_address, "action": "creation", "url": admin_url})
145 user_mail = "%s" %route.applier.email
146 user_mail = user_mail.split(';')
147 send_new_mail(settings.EMAIL_SUBJECT_PREFIX + "Rule %s creation request submitted by %s" %(route.name, route.applier.username),
148 mail_body, settings.SERVER_EMAIL, user_mail,
149 get_peer_techc_mails(route.applier))
150 d = { 'clientip' : "%s"%requesters_address, 'user' : route.applier.username }
151 logger.info(mail_body, extra=d)
152 return HttpResponseRedirect(reverse("group-routes"))
154 if not request.user.is_superuser:
155 form.fields['then'] = forms.ModelMultipleChoiceField(queryset=ThenAction.objects.filter(action__in=settings.UI_USER_THEN_ACTIONS).order_by('action'), required=True)
156 form.fields['protocol'] = forms.ModelMultipleChoiceField(queryset=MatchProtocol.objects.filter(protocol__in=settings.UI_USER_PROTOCOLS).order_by('protocol'), required=False)
157 return render_to_response('apply.html', {'form': form, 'applier':applier},
158 context_instance=RequestContext(request))
162 def edit_route(request, route_slug):
163 applier = request.user.pk
164 applier_peer = request.user.get_profile().peer
165 route_edit = get_object_or_404(Route, name=route_slug)
166 route_edit_applier_peer = route_edit.applier.get_profile().peer
167 if applier_peer != route_edit_applier_peer and (not request.user.is_superuser):
168 messages.add_message(request, messages.WARNING,
169 _("Insufficient rights to edit rule %s") %(route_slug))
170 return HttpResponseRedirect(reverse("group-routes"))
171 # if route_edit.status == "ADMININACTIVE" :
172 # messages.add_message(request, messages.WARNING,
173 # "Administrator has disabled editing of rule %s" %(route_slug))
174 # return HttpResponseRedirect(reverse("group-routes"))
175 # if route_edit.status == "EXPIRED" :
176 # messages.add_message(request, messages.WARNING,
177 # "Cannot edit the expired rule %s. Contact helpdesk to enable it" %(route_slug))
178 # return HttpResponseRedirect(reverse("group-routes"))
179 if route_edit.status == "PENDING" :
180 messages.add_message(request, messages.WARNING,
181 _("Cannot edit a pending rule: %s.") %(route_slug))
182 return HttpResponseRedirect(reverse("group-routes"))
183 route_original = deepcopy(route_edit)
185 request_data = request.POST.copy()
186 if request.user.is_superuser:
187 request_data['issuperuser'] = request.user.username
189 request_data['applier'] = applier
191 del request_data['issuperuser']
194 form = RouteForm(request_data, instance = route_edit)
195 critical_changed_values = ['source', 'destination', 'sourceport', 'destinationport', 'port', 'protocol', 'then']
197 changed_data = form.changed_data
198 route=form.save(commit=False)
199 route.name = route_original.name
200 route.status = route_original.status
201 route.response = route_original.response
202 if not request.user.is_superuser:
203 route.applier = request.user
204 if bool(set(changed_data) & set(critical_changed_values)) or (not route_original.status == 'ACTIVE'):
205 route.status = "PENDING"
206 route.response = "Applying"
207 route.source = IPNetwork("%s/%s" %(IPNetwork(route.source).network.compressed, IPNetwork(route.source).prefixlen)).compressed
208 route.destination = IPNetwork("%s/%s" %(IPNetwork(route.destination).network.compressed, IPNetwork(route.destination).prefixlen)).compressed
210 if bool(set(changed_data) & set(critical_changed_values)) or (not route_original.status == 'ACTIVE'):
213 requesters_address = request.META['HTTP_X_FORWARDED_FOR']
214 fqdn = Site.objects.get_current().domain
215 admin_url = "https://%s%s" % (fqdn, "/fod/edit/%s"%route.name)
216 mail_body = render_to_string("rule_action.txt",
217 {"route": route, "address": requesters_address, "action": "edit", "url": admin_url})
218 user_mail = "%s" %route.applier.email
219 user_mail = user_mail.split(';')
220 send_new_mail(settings.EMAIL_SUBJECT_PREFIX + "Rule %s edit request submitted by %s" %(route.name, route.applier.username),
221 mail_body, settings.SERVER_EMAIL, user_mail,
222 get_peer_techc_mails(route.applier))
223 d = { 'clientip' : requesters_address, 'user' : route.applier.username }
224 logger.info(mail_body, extra=d)
225 return HttpResponseRedirect(reverse("group-routes"))
227 if not request.user.is_superuser:
228 form.fields['then'] = forms.ModelMultipleChoiceField(queryset=ThenAction.objects.filter(action__in=settings.UI_USER_THEN_ACTIONS).order_by('action'), required=True)
229 form.fields['protocol'] = forms.ModelMultipleChoiceField(queryset=MatchProtocol.objects.filter(protocol__in=settings.UI_USER_PROTOCOLS).order_by('protocol'), required=False)
230 return render_to_response('apply.html', {'form': form, 'edit':True, 'applier': applier},
231 context_instance=RequestContext(request))
233 if (not route_original.status == 'ACTIVE'):
234 route_edit.expires = datetime.date.today() + datetime.timedelta(days = settings.EXPIRATION_DAYS_OFFSET)
235 dictionary = model_to_dict(route_edit, fields=[], exclude=[])
236 if request.user.is_superuser:
237 dictionary['issuperuser'] = request.user.username
240 del dictionary['issuperuser']
243 form = RouteForm(dictionary)
244 if not request.user.is_superuser:
245 form.fields['then'] = forms.ModelMultipleChoiceField(queryset=ThenAction.objects.filter(action__in=settings.UI_USER_THEN_ACTIONS).order_by('action'), required=True)
246 form.fields['protocol'] = forms.ModelMultipleChoiceField(queryset=MatchProtocol.objects.filter(protocol__in=settings.UI_USER_PROTOCOLS).order_by('protocol'), required=False)
247 return render_to_response('apply.html', {'form': form, 'edit':True, 'applier': applier},
248 context_instance=RequestContext(request))
252 def delete_route(request, route_slug):
253 if request.is_ajax():
254 route = get_object_or_404(Route, name=route_slug)
255 applier_peer = route.applier.get_profile().peer
256 requester_peer = request.user.get_profile().peer
257 if applier_peer == requester_peer or request.user.is_superuser:
258 route.status = "PENDING"
259 route.expires = datetime.date.today()
260 if not request.user.is_superuser:
261 route.applier = request.user
262 route.response = "Suspending"
264 route.commit_delete()
265 requesters_address = request.META['HTTP_X_FORWARDED_FOR']
266 fqdn = Site.objects.get_current().domain
267 admin_url = "https://%s%s" % (fqdn, "/fod/edit/%s"%route.name)
268 mail_body = render_to_string("rule_action.txt",
269 {"route": route, "address": requesters_address, "action": "removal", "url": admin_url})
270 user_mail = "%s" %route.applier.email
271 user_mail = user_mail.split(';')
272 send_new_mail(settings.EMAIL_SUBJECT_PREFIX + "Rule %s removal request submitted by %s" %(route.name, route.applier.username),
273 mail_body, settings.SERVER_EMAIL, user_mail,
274 get_peer_techc_mails(route.applier))
275 d = { 'clientip' : requesters_address, 'user' : route.applier.username }
276 logger.info(mail_body, extra=d)
277 html = "<html><body>Done</body></html>"
278 return HttpResponse(html)
280 return HttpResponseRedirect(reverse("group-routes"))
284 def user_profile(request):
287 peer = request.user.get_profile().peer
288 peers = Peer.objects.filter(pk=peer.pk)
289 if user.is_superuser:
290 peers = Peer.objects.all()
291 except UserProfile.DoesNotExist:
292 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
293 return render_to_response('error.html', {'error': error}, context_instance=RequestContext(request))
294 return render_to_response('profile.html', {'user': user, 'peers':peers},
295 context_instance=RequestContext(request))
298 def user_login(request):
300 error_username = False
301 error_orgname = False
302 error_entitlement = False
304 has_entitlement = False
306 username = request.META['HTTP_EPPN']
308 error_username = True
309 firstname = lookupShibAttr(settings.SHIB_FIRSTNAME, request.META)
310 lastname = lookupShibAttr(settings.SHIB_LASTNAME, request.META)
311 mail = lookupShibAttr(settings.SHIB_MAIL, request.META)
312 entitlement = lookupShibAttr(settings.SHIB_ENTITLEMENT, request.META)
313 #organization = request.META['HTTP_SHIB_HOMEORGANIZATION']
315 if settings.SHIB_AUTH_ENTITLEMENT in entitlement.split(";"):
316 has_entitlement = True
317 if not has_entitlement:
318 error_entitlement = True
319 # if not organization:
320 # error_orgname = True
324 error = _("Your idP should release the HTTP_EPPN attribute towards this service<br>")
326 # error = error + _("Your idP should release the HTTP_SHIB_HOMEORGANIZATION attribute towards this service<br>")
327 if error_entitlement:
328 error = error + _("Your idP should release an appropriate HTTP_SHIB_EP_ENTITLEMENT attribute towards this service<br>")
330 error = error + _("Your idP should release the HTTP_SHIB_INETORGPERSON_MAIL attribute towards this service")
331 if error_username or error_orgname or error_entitlement or error_mail:
332 return render_to_response('error.html', {'error': error, "missing_attributes": True},
333 context_instance=RequestContext(request))
335 user = User.objects.get(username__exact=username)
337 user.first_name = firstname
338 user.last_name = lastname
343 user = authenticate(username=username, firstname=firstname, lastname=lastname, mail=mail, authsource='shibboleth')
347 peer = user.get_profile().peer
348 # peer = Peer.objects.get(domain_name=organization)
349 # up = UserProfile.objects.get_or_create(user=user,peer=peer)
351 form = UserProfileForm()
352 form.fields['user'] = forms.ModelChoiceField(queryset=User.objects.filter(pk=user.pk), empty_label=None)
353 form.fields['peer'] = forms.ModelChoiceField(queryset=Peer.objects.all(), empty_label=None)
354 return render_to_response('registration/select_institution.html', {'form': form}, context_instance=RequestContext(request))
356 user_activation_notify(user)
359 return HttpResponseRedirect(reverse("group-routes"))
361 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
362 return render_to_response('error.html', {'error': error, 'inactive': True},
363 context_instance=RequestContext(request))
365 error = _("Something went wrong during user authentication. Contact your administrator")
366 return render_to_response('error.html', {'error': error,},
367 context_instance=RequestContext(request))
368 except User.DoesNotExist as e:
369 error = _("Invalid login procedure. Error: %s" %e)
370 return render_to_response('error.html', {'error': error,},
371 context_instance=RequestContext(request))
372 # Return an 'invalid login' error message.
373 # return HttpResponseRedirect(reverse("user-routes"))
375 def user_activation_notify(user):
376 current_site = Site.objects.get_current()
377 peer = user.get_profile().peer
380 # Email subject *must not* contain newlines
381 # TechCs will be notified about new users.
382 # Platform admins will activate the users.
383 subject = render_to_string('registration/activation_email_subject.txt',
384 { 'site': current_site })
385 subject = ''.join(subject.splitlines())
386 registration_profile = RegistrationProfile.objects.create_profile(user)
387 message = render_to_string('registration/activation_email.txt',
388 { 'activation_key': registration_profile.activation_key,
389 'expiration_days': settings.ACCOUNT_ACTIVATION_DAYS,
390 'site': current_site,
392 if settings.NOTIFY_ADMIN_MAILS:
393 admin_mails = settings.NOTIFY_ADMIN_MAILS
394 send_new_mail(settings.EMAIL_SUBJECT_PREFIX + subject,
395 message, settings.SERVER_EMAIL,
398 # Mail to domain techCs plus platform admins (no activation hash sent)
399 subject = render_to_string('registration/activation_email_peer_notify_subject.txt',
400 { 'site': current_site,
402 subject = ''.join(subject.splitlines())
403 message = render_to_string('registration/activation_email_peer_notify.txt',
406 send_new_mail(settings.EMAIL_SUBJECT_PREFIX + subject,
407 message, settings.SERVER_EMAIL,
408 get_peer_techc_mails(user), [])
412 def add_rate_limit(request):
413 if request.method == "GET":
414 form = ThenPlainForm()
415 return render_to_response('add_rate_limit.html', {'form': form,},
416 context_instance=RequestContext(request))
419 form = ThenPlainForm(request.POST)
421 then=form.save(commit=False)
422 then.action_value = "%sk"%then.action_value
425 response_data['pk'] = "%s" %then.pk
426 response_data['value'] = "%s:%s" %(then.action, then.action_value)
427 return HttpResponse(json.dumps(response_data), mimetype='application/json')
429 return render_to_response('add_rate_limit.html', {'form': form,},
430 context_instance=RequestContext(request))
434 def add_port(request):
435 if request.method == "GET":
436 form = PortPlainForm()
437 return render_to_response('add_port.html', {'form': form,},
438 context_instance=RequestContext(request))
441 form = PortPlainForm(request.POST)
445 response_data['value'] = "%s" %port.pk
446 response_data['text'] = "%s" %port.port
447 return HttpResponse(json.dumps(response_data), mimetype='application/json')
449 return render_to_response('add_port.html', {'form': form,},
450 context_instance=RequestContext(request))
453 def selectinst(request):
454 if request.method == 'POST':
455 request_data = request.POST.copy()
456 user = request_data['user']
458 existingProfile = UserProfile.objects.get(user=user)
459 error = _("Violation warning: User account is already associated with an institution.The event has been logged and our administrators will be notified about it")
460 return render_to_response('error.html', {'error': error, 'inactive': True},
461 context_instance=RequestContext(request))
462 except UserProfile.DoesNotExist:
465 form = UserProfileForm(request_data)
467 userprofile = form.save()
468 user_activation_notify(userprofile.user)
469 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
470 return render_to_response('error.html', {'error': error, 'inactive': True},
471 context_instance=RequestContext(request))
473 form.fields['user'] = forms.ModelChoiceField(queryset=User.objects.filter(pk=user.pk), empty_label=None)
474 form.fields['institution'] = forms.ModelChoiceField(queryset=Peer.objects.all(), empty_label=None)
475 return render_to_response('registration/select_institution.html', {'form': form}, context_instance=RequestContext(request))
478 def overview(request):
480 if user.is_authenticated():
481 if user.has_perm('accounts.overview'):
482 users = User.objects.all()
483 group_routes = Route.objects.all()
484 return render_to_response('overview/index.html', {'users': users, 'routes': group_routes},
485 context_instance=RequestContext(request))
488 return render_to_response('overview/index.html', {'violation': violation},
489 context_instance=RequestContext(request))
491 return HttpResponseRedirect(reverse("altlogin"))
495 def user_logout(request):
497 return HttpResponseRedirect(reverse('group-routes'))
500 def load_jscript(request, file):
501 long_polling_timeout = int(settings.POLL_SESSION_UPDATE)*1000 + 10000
502 return render_to_response('%s.js' % file, {'timeout': long_polling_timeout}, context_instance=RequestContext(request), mimetype="text/javascript")
505 def get_peer_techc_mails(user):
509 user_mail = "%s" %user.email
510 user_mail = user_mail.split(';')
511 techmails = user.get_profile().peer.techc_emails.all()
513 for techmail in techmails:
514 techmails_list.append(techmail.email)
515 if settings.NOTIFY_ADMIN_MAILS:
516 additional_mail = settings.NOTIFY_ADMIN_MAILS
517 mail.extend(additional_mail)
518 mail.extend(techmails_list)
521 def send_new_mail(subject, message, from_email, recipient_list, bcc_list):
522 return EmailMessage(subject, message, from_email, recipient_list, bcc_list).send()
525 def lookupShibAttr(attrmap, requestMeta):
527 if (attr in requestMeta.keys()):
528 if len(requestMeta[attr]) > 0:
529 return requestMeta[attr]