1 # -*- coding: utf-8 -*-
3 # portal/views.py: views for the portal application
4 # This file is part of the Manifold project.
7 # Jordan Augé <jordan.auge@lip6.fr>
8 # Mohammed Yasin Rahman <mohammed-yasin.rahman@lip6.fr>
9 # Copyright 2013, UPMC Sorbonne Universités / LIP6
11 # This program is free software; you can redistribute it and/or modify it under
12 # the terms of the GNU General Public License as published by the Free Software
13 # Foundation; either version 3, or (at your option) any later version.
15 # This program is distributed in the hope that it will be useful, but WITHOUT
16 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17 # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
20 # You should have received a copy of the GNU General Public License along with
21 # this program; see the file COPYING. If not, write to the Free Software
22 # Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 from django.views.generic.base import TemplateView
28 from django.shortcuts import render
29 from django.template.loader import render_to_string
30 from django.core.mail import send_mail
31 from django.utils.decorators import method_decorator
32 from django.contrib.auth.decorators import login_required
34 from plugins.lists.simplelist import SimpleList
35 from plugins.hazelnut import Hazelnut
36 from plugins.pres_view import PresView
37 from portal.event import Event
39 from portal import signals
40 from portal.forms import SliceRequestForm
41 from portal.util import RegistrationView, ActivationView
42 from portal.models import PendingUser, PendingSlice
43 from portal.actions import authority_get_pi_emails, get_request_by_authority, manifold_add_user, manifold_update_user
44 from manifold.manifoldapi import execute_query
45 from manifold.core.query import Query
46 from unfold.page import Page
47 from myslice.viewutils import topmenu_items, the_user
48 from django.http import HttpResponseRedirect, HttpResponse
51 def register_4m_f4f(request):
54 authorities_query = Query.get('authority').filter_by('authority_hrn', 'included', ['ple.inria', 'ple.upmc']).select('name', 'authority_hrn')
55 #authorities_query = Query.get('authority').select('authority_hrn')
56 authorities = execute_query(request, authorities_query)
58 if request.method == 'POST':
59 # We shall use a form here
61 #get_email = PendingUser.objects.get(email)
62 reg_fname = request.POST.get('firstname', '')
63 reg_lname = request.POST.get('lastname', '')
64 #reg_aff = request.POST.get('affiliation','')
65 reg_auth = request.POST.get('authority_hrn', '')
66 reg_email = request.POST.get('email','').lower()
68 #POST value validation
69 if (re.search(r'^[\w+\s.@+-]+$', reg_fname)==None):
70 errors.append('First Name may contain only letters, numbers, spaces and @/./+/-/_ characters.')
71 #return HttpResponse("Only Letters, Numbers, - and _ allowd in First Name")
72 #return render(request, 'register_4m_f4f.html')
73 if (re.search(r'^[\w+\s.@+-]+$', reg_lname) == None):
74 errors.append('Last Name may contain only letters, numbers, spaces and @/./+/-/_ characters.')
75 #return HttpResponse("Only Letters, Numbers, - and _ is allowed in Last name")
76 #return render(request, 'register_4m_f4f.html')
77 # if (re.search(r'^[\w+\s.@+-]+$', reg_aff) == None):
78 # errors.append('Affiliation may contain only letters, numbers, spaces and @/./+/-/_ characters.')
79 #return HttpResponse("Only Letters, Numbers and _ is allowed in Affiliation")
80 #return render(request, 'register_4m_f4f.html')
81 # XXX validate authority hrn !!
82 if PendingUser.objects.filter(email__iexact=reg_email):
83 errors.append('Email already registered.Please provide a new email address.')
84 #return HttpResponse("Email Already exists")
85 #return render(request, 'register_4m_f4f.html')
86 if 'generate' in request.POST['question']:
87 # Generate public and private keys using SFA Library
88 from sfa.trust.certificate import Keypair
89 k = Keypair(create=True)
90 public_key = k.get_pubkey_string()
91 private_key = k.as_pem()
92 private_key = ''.join(private_key.split())
93 public_key = "ssh-rsa " + public_key
95 keypair = '{"user_public_key":"'+ public_key + '", "user_private_key":"'+ private_key + '"}'
96 # keypair = re.sub("\r", "", keypair)
97 # keypair = re.sub("\n", "\\n", keypair)
98 # #keypair = keypair.rstrip('\r\n')
99 # keypair = ''.join(keypair.split())
101 up_file = request.FILES['user_public_key']
102 file_content = up_file.read()
103 file_name = up_file.name
104 file_extension = os.path.splitext(file_name)[1]
105 allowed_extension = ['.pub','.txt']
106 if file_extension in allowed_extension and re.search(r'ssh-rsa',file_content):
107 keypair = '{"user_public_key":"'+ file_content +'"}'
108 keypair = re.sub("\r", "", keypair)
109 keypair = re.sub("\n", "\\n",keypair)
110 keypair = ''.join(keypair.split())
112 errors.append('Please upload a valid RSA public key [.txt or .pub].')
114 #b = PendingUser(first_name=reg_fname, last_name=reg_lname, affiliation=reg_aff,
115 # email=reg_email, password=request.POST['password'], keypair=keypair)
119 first_name=reg_fname,
121 #affiliation=reg_aff,
122 authority_hrn=reg_auth,
124 password=request.POST['password'],
131 first_name : reg_fname,
132 last_name : reg_lname,
133 #affiliation : reg_aff,
134 authority_hrn: reg_auth,
137 cc_myself : True # form.cleaned_data['cc_myself']
140 recipients = authority_get_pi_emails(authority_hrn)
142 recipients.append(ctx['email'])
144 msg = render_to_string('user_request_email.txt', ctx)
145 send_mail("Onelab New User request submitted", msg, email, recipients)
147 return render(request, 'user_register_complete.html')
149 return render(request, 'register_4m_f4f.html',{
150 'topmenu_items': topmenu_items('Register', request),
152 'firstname': request.POST.get('firstname', ''),
153 'lastname': request.POST.get('lastname', ''),
154 #'affiliation': request.POST.get('affiliation', ''),
155 'authority_hrn': request.POST.get('authority_hrn', ''),
156 'email': request.POST.get('email', ''),
157 'password': request.POST.get('password', ''),
158 'authorities': authorities
163 def slice_request(request):
166 authorities_query = Query.get('authority').filter_by('authority_hrn', 'included', ['ple.inria', 'ple.upmc']).select('name', 'authority_hrn')
167 #authorities_query = Query.get('authority').select('authority_hrn')
168 authorities = execute_query(request, authorities_query)
170 authority_hrn_tuple = []
171 for authority in authorities:
172 authority_hrn_tuple.append((authority['authority_hrn'], authority['name']))
173 authority_hrn_initial = {'authority_hrn': authority_hrn_tuple}
175 # request.POST or None ?
176 if request.method == 'POST':
177 # The form has been submitted
178 form = SliceRequestForm(request.POST, initial=authority_hrn_initial)
181 slice_name = form.cleaned_data['slice_name']
182 authority_hrn = form.cleaned_data['authority_hrn']
183 number_of_nodes = form.cleaned_data['number_of_nodes']
184 type_of_nodes = form.cleaned_data['type_of_nodes']
185 purpose = form.cleaned_data['purpose']
188 slice_name = slice_name,
189 authority_hrn = authority_hrn,
190 number_of_nodes = number_of_nodes,
191 type_of_nodes = type_of_nodes,
196 # All validation rules pass; process data in form.cleaned_data
197 # slice_name, number_of_nodes, type_of_nodes, purpose
198 email = form.cleaned_data['email'] # email of the sender
199 cc_myself = form.cleaned_data['cc_myself']
201 # The recipients are the PI of the authority
202 recipients = authority_get_pi_emails(authority_hrn)
203 #recipients = ['yasin.upmc@gmail.com','jordan.auge@lip6.fr']
205 recipients.append(email)
206 msg = render_to_string('slice_request_email.txt', form.cleaned_data)
207 send_mail("Onelab New Slice request form submitted", msg, email, recipients)
209 return render(request,'slicereq_recvd.html') # Redirect after POST
211 form = SliceRequestForm(initial=authority_hrn_initial)
214 # template_env['form'] = form
215 # template_env['topmenu_items'] = topmenu_items('Request a slice', request)
216 # template_env['unfold1_main'] = render(request, 'slice_request_.html', {
219 # from django.shortcuts import render_to_response
220 # from django.template import RequestContext
221 # return render_to_response ('view-unfold1.html',template_env,
222 # context_instance=RequestContext(request))
224 return render(request, 'slice_request.html', {
226 'topmenu_items': topmenu_items('Request a slice', request),
227 'username': the_user (request)
231 class PresViewView(TemplateView):
232 template_name = "view-unfold1.html"
234 def get_context_data(self, **kwargs):
236 page = Page(self.request)
238 pres_view = PresView(page = page)
240 context = super(PresViewView, self).get_context_data(**kwargs)
242 #context['ALL_STATIC'] = "all_static"
243 context['unfold1_main'] = pres_view.render(self.request)
245 # XXX This is repeated in all pages
246 # more general variables expected in the template
247 context['title'] = 'Test view that combines various plugins'
248 # the menu items on the top
249 context['topmenu_items'] = topmenu_items('PresView', self.request)
250 # so we can sho who is logged
251 context['username'] = the_user(self.request)
253 prelude_env = page.prelude_env()
254 context.update(prelude_env)
258 def json_me(config_file,type):
260 for ligne in config_file:
261 if not ligne.startswith('#'):
262 args = ligne.split(';')
263 json_answer += str('{ "name": "' + args[0] + '" ,"id":"' + args[1] + '" ,"descriptif":"' + args[2]+'"')
265 json_answer += str(',"contraints":')
267 json_answer += str('""')
269 json_answer += str(args[3])
270 json_answer += str('},')
271 return json_answer[:-1]
274 DIR = '/var/myslice/'
275 STATIC = '%s/config_method_static' % DIR
276 DYNAMIC = '%s/config_method_dynamic' % DIR
277 ANIMATION = '%s/config_method_animation' % DIR
279 def pres_view_methods(request, type):
283 elif type =="static":
284 config = open(STATIC, "r")
285 json_answer = str('{ "options": [')
286 json_answer += str(json_me(config,"static"))
287 json_answer += str('] }')
289 elif type =="dynamic":
290 config = open(DYNAMIC, "r")
291 json_answer = str('{ "options": [')
292 json_answer += str(json_me(config,"dynamic"))
293 json_answer += str('] }')
295 elif type =="animation":
296 config = open(ANIMATION, "r")
297 json_answer = str('{ "options": [')
298 json_answer += str(json_me(config,"animation"))
299 json_answer += str('] }')
302 config = open(STATIC, "r")
303 json_answer = str('{ "static": [')
304 json_answer += str(json_me(config,"static"))
305 json_answer += str('],')
306 json_answer += str('"dynamic": [')
308 config = open(DYNAMIC, "r")
309 json_answer += str(json_me(config,"dynamic"))
310 json_answer += str('],')
311 json_answer += str('"animation": [')
313 config = open(ANIMATION, "r")
314 json_answer += str(json_me(config,"animation"))
315 json_answer += str('] }')
319 return HttpResponse (json_answer, mimetype="application/json")
321 def pres_view_animation(request, constraints, id):
323 # sites crees depuis 2008
324 # static.py?contraints=']date_created':1262325600&id='name_id"'
326 # method = request.getvalue('method') #ex : GetSites
327 #constraints = "']date_created':1262325600"
333 # method = 'GetSites'#request.getvalue('method') #ex : GetSites
334 # constraints = {}#request.getvalue('constraints') // nul = {}
335 # response_field = "'site_id','name','date_created'"#request.getvalue('response_field')
337 config_file = open(ANIMATION, "r")
338 for ligne in config_file:
339 if not ligne.startswith('#'):
340 ligne = ligne.split('\n')
341 first = ligne[0].split(';')
342 if (str(first[1]) == str(id)):
346 #Les print_method, print_option sont definis par le client (js)
347 #Les animations acceptent que les connexions anonymous
348 # args = "postmsg;animation;;;anonymous;https://www.planet-lab.eu/PLCAPI/;"
349 args = ";;"+str(save[8])+";"+str(save[9])+";anonymous;"+str(save[5])+";"+str(save[6])+";{"+str(constraints)+"};"+str(save[7])+";"
352 #Creation d'un objet event
356 "print_options": event.print_options,
357 "print_method": event.print_method,
358 "message": event.data
363 json_answer = json.dumps(cmd)
364 return HttpResponse (json_answer, mimetype="application/json")
366 def pres_view_static(request, constraints, id):
367 #constraints = "']date_created':1262325600"
370 # method = 'GetSites'#request.getvalue('method') #ex : GetSites
371 # constraints = {}#request.getvalue('constraints') // nul = {}
372 # response_field = "'site_id','name','date_created'"#request.getvalue('response_field')
374 config_file = open(STATIC, "r")
375 for ligne in config_file:
376 if not ligne.startswith('#'):
377 ligne = ligne.split('\n')
378 first = ligne[0].split(';')
379 if (str(first[1]) == str(id)):
383 #Les print_method, print_option sont definis par le client (js)
384 #Les animations acceptent que les connexions anonymous
385 # args = "postmsg;animation;;;anonymous;https://www.planet-lab.eu/PLCAPI/;"
386 args = ";;"+str(save[8])+";"+str(save[9])+";anonymous;"+str(save[5])+";"+str(save[6])+";{"+str(constraints)+"};"+str(save[7])+";"
389 #Creation d'un objet event
393 "print_options": event.print_options,
394 "print_method": event.print_method,
395 "message": event.data
400 json_answer = json.dumps(cmd)
401 return HttpResponse (json_answer, mimetype="application/json")
403 class ValidatePendingView(TemplateView):
404 template_name = "validate_pending.html"
406 def get_context_data(self, **kwargs):
407 # We might have slices on different registries with different user accounts
408 # We note that this portal could be specific to a given registry, to which we register users, but i'm not sure that simplifies things
409 # Different registries mean different identities, unless we identify via SFA HRN or have associated the user email to a single hrn
411 #messages.info(self.request, 'You have logged in')
412 page = Page(self.request)
414 ctx_my_authorities = {}
415 ctx_delegation_authorities = {}
418 # The user need to be logged in
419 if the_user(self.request):
420 # Who can a PI validate:
421 # His own authorities + those he has credentials for.
422 # In MySlice we need to look at credentials also.
425 # XXX This will have to be asynchroneous. Need to implement barriers,
426 # for now it will be sufficient to have it working statically
428 # get user_id to later on query accounts
429 # XXX Having real query plan on local tables would simplify all this
430 # XXX $user_email is still not available for local tables
431 #user_query = Query().get('local:user').filter_by('email', '==', '$user_email').select('user_id')
432 user_query = Query().get('local:user').filter_by('email', '==', the_user(self.request)).select('user_id')
433 user, = execute_query(self.request, user_query)
434 user_id = user['user_id']
436 # Query manifold to learn about available SFA platforms for more information
437 # In general we will at least have the portal
438 # For now we are considering all registries
441 sfa_platforms_query = Query().get('local:platform').filter_by('gateway_type', '==', 'sfa').select('platform_id', 'platform', 'auth_type')
442 sfa_platforms = execute_query(self.request, sfa_platforms_query)
443 for sfa_platform in sfa_platforms:
444 print "SFA PLATFORM > ", sfa_platform['platform']
445 if not 'auth_type' in sfa_platform:
447 auth = sfa_platform['auth_type']
448 if not auth in all_authorities:
449 all_authorities.append(auth)
450 platform_ids.append(sfa_platform['platform_id'])
452 # We can check on which the user has authoritity credentials = PI rights
453 credential_authorities = set()
454 credential_authorities_expired = set()
456 # User account on these registries
457 user_accounts_query = Query.get('local:account').filter_by('user_id', '==', user_id).filter_by('platform_id', 'included', platform_ids).select('config')
458 user_accounts = execute_query(self.request, user_accounts_query)
462 for user_account in user_accounts:
463 config = json.loads(user_account['config'])
465 if 'authority_credentials' in config:
466 for authority_hrn, credential in config['authority_credentials'].items():
467 #if credential is not expired:
468 credential_authorities.add(authority_hrn)
470 # credential_authorities_expired.add(authority_hrn)
471 if 'delegated_authority_credentials' in config:
472 for authority_hrn, credential in config['delegated_authority_credentials'].items():
473 #if credential is not expired:
474 credential_authorities.add(authority_hrn)
476 # credential_authorities_expired.add(authority_hrn)
478 print 'credential_authorities =', credential_authorities
479 print 'credential_authorities_expired =', credential_authorities_expired
481 # ** Where am I a PI **
482 # For this we need to ask SFA (of all authorities) = PI function
483 pi_authorities_query = Query.get('user').filter_by('user_hrn', '==', '$user_hrn').select('pi_authorities')
484 pi_authorities_tmp = execute_query(self.request, pi_authorities_query)
485 pi_authorities = set()
486 for pa in pi_authorities_tmp:
487 pi_authorities |= set(pa['pi_authorities'])
489 print "pi_authorities =", pi_authorities
491 # My authorities + I have a credential
492 pi_credential_authorities = pi_authorities & credential_authorities
493 pi_no_credential_authorities = pi_authorities - credential_authorities - credential_authorities_expired
494 pi_expired_credential_authorities = pi_authorities & credential_authorities_expired
495 # Authorities I've been delegated PI rights
496 pi_delegation_credential_authorities = credential_authorities - pi_authorities
497 pi_delegation_expired_authorities = credential_authorities_expired - pi_authorities
499 print "pi_credential_authorities =", pi_credential_authorities
500 print "pi_no_credential_authorities =", pi_no_credential_authorities
501 print "pi_expired_credential_authorities =", pi_expired_credential_authorities
502 print "pi_delegation_credential_authorities = ", pi_delegation_credential_authorities
503 print "pi_delegation_expired_authorities = ", pi_delegation_expired_authorities
505 # Summary intermediary
506 pi_my_authorities = pi_credential_authorities | pi_no_credential_authorities | pi_expired_credential_authorities
507 pi_delegation_authorities = pi_delegation_credential_authorities | pi_delegation_expired_authorities
510 print "pi_my_authorities = ", pi_my_authorities
511 print "pi_delegation_authorities = ", pi_delegation_authorities
514 queried_pending_authorities = pi_my_authorities | pi_delegation_authorities
516 print "queried_pending_authorities = ", queried_pending_authorities
518 requests = get_request_by_authority(queried_pending_authorities)
519 for request in requests:
520 auth_hrn = request['authority_hrn']
522 if auth_hrn in pi_my_authorities:
523 dest = ctx_my_authorities
525 # define the css class
526 if auth_hrn in pi_credential_authorities:
527 request['allowed'] = 'allowed'
528 elif auth_hrn in pi_expired_credential_authorities:
529 request['allowed'] = 'expired'
530 else: # pi_no_credential_authorities
531 request['allowed'] = 'denied'
533 elif auth_hrn in pi_delegation_authorities:
534 dest = ctx_delegation_authorities
536 if auth_hrn in pi_delegation_credential_authorities:
537 request['allowed'] = 'allowed'
538 else: # pi_delegation_expired_authorities
539 request['allowed'] = 'expired'
544 if not auth_hrn in dest:
546 dest[auth_hrn].append(request)
548 context = super(ValidatePendingView, self).get_context_data(**kwargs)
549 context['my_authorities'] = ctx_my_authorities
550 context['delegation_authorities'] = ctx_delegation_authorities
552 # XXX This is repeated in all pages
553 # more general variables expected in the template
554 context['title'] = 'Test view that combines various plugins'
555 # the menu items on the top
556 context['topmenu_items'] = topmenu_items('Dashboard', self.request)
557 # so we can sho who is logged
558 context['username'] = the_user(self.request)
560 # XXX We need to prepare the page for queries
561 #context.update(page.prelude_env())