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 import View
28 from django.views.generic.base import TemplateView
29 from django.shortcuts import render
30 from django.template.loader import render_to_string
31 from django.core.mail import send_mail
32 from django.utils.decorators import method_decorator
33 from django.contrib.auth.decorators import login_required
35 from plugins.lists.simplelist import SimpleList
36 from plugins.hazelnut import Hazelnut
37 from plugins.pres_view import PresView
38 from portal.event import Event
40 from portal import signals
41 from portal.forms import SliceRequestForm, ContactForm
42 from portal.util import RegistrationView, ActivationView
43 from portal.models import PendingUser, PendingSlice
44 from portal.actions import authority_get_pi_emails, get_request_by_authority, manifold_add_user, manifold_update_user
45 from manifold.manifoldapi import execute_query
46 from manifold.core.query import Query
47 from unfold.page import Page
48 from myslice.viewutils import topmenu_items, the_user
49 from django.http import HttpResponseRedirect, HttpResponse
53 #my_acc form value processing
54 def account_process(request):
55 user_query = Query().get('local:user').select('password','config')
56 user_details = execute_query(request, user_query)
58 if 'submit_name' in request.POST:
59 edited_first_name = request.POST['fname']
60 edited_last_name = request.POST['lname']
63 for user_config in user_details:
64 #email = user_detail['email']
65 if user_config['config']:
66 config = json.loads(user_config['config'])
67 config['firstname'] = edited_first_name
68 config['lastname'] = edited_last_name
69 config['authority'] = config.get('authority','Unknown Authority')
70 updated_config = json.dumps(config)
72 # updating config local:user in manifold
73 user_params = { 'config': updated_config}
74 manifold_update_user(request,user_params)
75 # this will be depricated, we will show the success msg in same page
76 return HttpResponse('Sucess: First Name and Last Name Updated!')
77 elif 'submit_pass' in request.POST:
78 edited_password = request.POST['password']
80 for user_pass in user_details:
81 user_pass['password'] = edited_password
82 #updating password in local:user
83 user_params = { 'password': user_pass['password']}
84 manifold_update_user(request,user_params)
86 return HttpResponse('Success: Password Changed!!')
87 elif 'generate' in request.POST:
88 # Generate public and private keys using SFA Library
89 from sfa.trust.certificate import Keypair
90 k = Keypair(create=True)
91 public_key = k.get_pubkey_string()
92 private_key = k.as_pem()
93 private_key = ''.join(private_key.split())
94 public_key = "ssh-rsa " + public_key
96 keypair = '{"user_public_key":"'+ public_key + '", "user_private_key":"'+ private_key + '"}'
97 # keypair = re.sub("\r", "", keypair)
98 # keypair = re.sub("\n", "\\n", keypair)
99 # #keypair = keypair.rstrip('\r\n')
100 # keypair = ''.join(keypair.split())
101 get_user.keypair = keypair
103 return HttpResponse('Success: New Keypair Generated! %s' % keypair)
105 elif 'upload_key' in request.POST:
106 up_file = request.FILES['pubkey']
107 file_content = up_file.read()
108 file_name = up_file.name
109 file_extension = os.path.splitext(file_name)[1]
110 allowed_extension = ['.pub','.txt']
111 if file_extension in allowed_extension and re.search(r'ssh-rsa',file_content):
112 file_content = '{"user_public_key":"'+ file_content +'"}'
113 file_content = re.sub("\r", "", file_content)
114 file_content = re.sub("\n", "\\n",file_content)
115 file_content = ''.join(file_content.split())
116 get_user.keypair = file_content
118 return HttpResponse('Success: Publickey uploaded! Old records overwritten')
120 return HttpResponse('Please upload a valid RSA public key [.txt or .pub].')
123 message = 'You submitted an empty form.'
124 return HttpResponse(message)
126 def register_4m_f4f(request):
129 authorities_query = Query.get('authority').filter_by('authority_hrn', 'included', ['ple.inria', 'ple.upmc']).select('name', 'authority_hrn')
130 #authorities_query = Query.get('authority').select('authority_hrn')
131 authorities = execute_query(request, authorities_query)
133 if request.method == 'POST':
134 # We shall use a form here
136 #get_email = PendingUser.objects.get(email)
137 reg_fname = request.POST.get('firstname', '')
138 reg_lname = request.POST.get('lastname', '')
139 #reg_aff = request.POST.get('affiliation','')
140 reg_auth = request.POST.get('authority_hrn', '')
141 reg_email = request.POST.get('email','').lower()
143 #POST value validation
144 if (re.search(r'^[\w+\s.@+-]+$', reg_fname)==None):
145 errors.append('First Name may contain only letters, numbers, spaces and @/./+/-/_ characters.')
146 #return HttpResponse("Only Letters, Numbers, - and _ allowd in First Name")
147 #return render(request, 'register_4m_f4f.html')
148 if (re.search(r'^[\w+\s.@+-]+$', reg_lname) == None):
149 errors.append('Last Name may contain only letters, numbers, spaces and @/./+/-/_ characters.')
150 #return HttpResponse("Only Letters, Numbers, - and _ is allowed in Last name")
151 #return render(request, 'register_4m_f4f.html')
152 # if (re.search(r'^[\w+\s.@+-]+$', reg_aff) == None):
153 # errors.append('Affiliation may contain only letters, numbers, spaces and @/./+/-/_ characters.')
154 #return HttpResponse("Only Letters, Numbers and _ is allowed in Affiliation")
155 #return render(request, 'register_4m_f4f.html')
156 # XXX validate authority hrn !!
157 if PendingUser.objects.filter(email__iexact=reg_email):
158 errors.append('Email already registered.Please provide a new email address.')
159 #return HttpResponse("Email Already exists")
160 #return render(request, 'register_4m_f4f.html')
161 if 'generate' in request.POST['question']:
162 # Generate public and private keys using SFA Library
163 from sfa.trust.certificate import Keypair
164 k = Keypair(create=True)
165 public_key = k.get_pubkey_string()
166 private_key = k.as_pem()
167 private_key = ''.join(private_key.split())
168 public_key = "ssh-rsa " + public_key
170 keypair = '{"user_public_key":"'+ public_key + '", "user_private_key":"'+ private_key + '"}'
171 # keypair = re.sub("\r", "", keypair)
172 # keypair = re.sub("\n", "\\n", keypair)
173 # #keypair = keypair.rstrip('\r\n')
174 # keypair = ''.join(keypair.split())
176 up_file = request.FILES['user_public_key']
177 file_content = up_file.read()
178 file_name = up_file.name
179 file_extension = os.path.splitext(file_name)[1]
180 allowed_extension = ['.pub','.txt']
181 if file_extension in allowed_extension and re.search(r'ssh-rsa',file_content):
182 keypair = '{"user_public_key":"'+ file_content +'"}'
183 keypair = re.sub("\r", "", keypair)
184 keypair = re.sub("\n", "\\n",keypair)
185 keypair = ''.join(keypair.split())
187 errors.append('Please upload a valid RSA public key [.txt or .pub].')
189 #b = PendingUser(first_name=reg_fname, last_name=reg_lname, affiliation=reg_aff,
190 # email=reg_email, password=request.POST['password'], keypair=keypair)
194 first_name=reg_fname,
196 #affiliation=reg_aff,
197 authority_hrn=reg_auth,
199 password=request.POST['password'],
206 first_name : reg_fname,
207 last_name : reg_lname,
208 #affiliation : reg_aff,
209 authority_hrn: reg_auth,
212 cc_myself : True # form.cleaned_data['cc_myself']
215 recipients = authority_get_pi_emails(authority_hrn)
217 recipients.append(ctx['email'])
219 msg = render_to_string('user_request_email.txt', ctx)
220 send_mail("Onelab New User request submitted", msg, email, recipients)
222 return render(request, 'user_register_complete.html')
224 return render(request, 'register_4m_f4f.html',{
225 'topmenu_items': topmenu_items('Register', request),
227 'firstname': request.POST.get('firstname', ''),
228 'lastname': request.POST.get('lastname', ''),
229 #'affiliation': request.POST.get('affiliation', ''),
230 'authority_hrn': request.POST.get('authority_hrn', ''),
231 'email': request.POST.get('email', ''),
232 'password': request.POST.get('password', ''),
233 'authorities': authorities
237 # view for contact form
238 def contact(request):
239 if request.method == 'POST': # If the form has been submitted...
240 form = ContactForm(request.POST) # A form bound to the POST data
241 if form.is_valid(): # All validation rules pass
242 # Process the data in form.cleaned_data
243 first_name = form.cleaned_data['first_name']
244 last_name = form.cleaned_data['last_name']
245 affiliation = form.cleaned_data['affiliation']
246 subject = form.cleaned_data['subject']
247 message = form.cleaned_data['message']
248 email = form.cleaned_data['email'] # email of the sender
249 cc_myself = form.cleaned_data['cc_myself']
251 #recipients = authority_get_pi_emails(authority_hrn)
252 recipients = ['yasin.upmc@gmail.com']
254 recipients.append(email)
256 from django.core.mail import send_mail
257 send_mail("Onelab user submitted a query ", [first_name,last_name,affiliation,subject,message], email, recipients)
258 return render(request,'contact_sent.html') # Redirect after POST
260 form = ContactForm() # An unbound form
262 return render(request, 'contact.html', {
264 'topmenu_items': topmenu_items('Contact Us', request),
265 'username': the_user (request)
270 def slice_request(request):
273 authorities_query = Query.get('authority').filter_by('authority_hrn', 'included', ['ple.inria', 'ple.upmc']).select('name', 'authority_hrn')
274 #authorities_query = Query.get('authority').select('authority_hrn')
275 authorities = execute_query(request, authorities_query)
277 authority_hrn_tuple = []
278 for authority in authorities:
279 authority_hrn_tuple.append((authority['authority_hrn'], authority['name']))
280 authority_hrn_initial = {'authority_hrn': authority_hrn_tuple}
282 # request.POST or None ?
283 if request.method == 'POST':
284 # The form has been submitted
285 form = SliceRequestForm(request.POST, initial=authority_hrn_initial)
288 slice_name = form.cleaned_data['slice_name']
289 authority_hrn = form.cleaned_data['authority_hrn']
290 number_of_nodes = form.cleaned_data['number_of_nodes']
291 type_of_nodes = form.cleaned_data['type_of_nodes']
292 purpose = form.cleaned_data['purpose']
295 slice_name = slice_name,
296 authority_hrn = authority_hrn,
297 number_of_nodes = number_of_nodes,
298 type_of_nodes = type_of_nodes,
303 # All validation rules pass; process data in form.cleaned_data
304 # slice_name, number_of_nodes, type_of_nodes, purpose
305 email = form.cleaned_data['email'] # email of the sender
306 cc_myself = form.cleaned_data['cc_myself']
308 # The recipients are the PI of the authority
309 recipients = authority_get_pi_emails(authority_hrn)
310 #recipients = ['yasin.upmc@gmail.com','jordan.auge@lip6.fr']
312 recipients.append(email)
313 msg = render_to_string('slice_request_email.txt', form.cleaned_data)
314 send_mail("Onelab New Slice request form submitted", msg, email, recipients)
316 return render(request,'slicereq_recvd.html') # Redirect after POST
318 form = SliceRequestForm(initial=authority_hrn_initial)
321 # template_env['form'] = form
322 # template_env['topmenu_items'] = topmenu_items('Request a slice', request)
323 # template_env['unfold1_main'] = render(request, 'slice_request_.html', {
326 # from django.shortcuts import render_to_response
327 # from django.template import RequestContext
328 # return render_to_response ('view-unfold1.html',template_env,
329 # context_instance=RequestContext(request))
331 return render(request, 'slice_request.html', {
333 'topmenu_items': topmenu_items('Request a slice', request),
334 'username': the_user (request)
338 class PresViewView(TemplateView):
339 template_name = "view-unfold1.html"
341 def get_context_data(self, **kwargs):
343 page = Page(self.request)
345 pres_view = PresView(page = page)
347 context = super(PresViewView, self).get_context_data(**kwargs)
349 #context['ALL_STATIC'] = "all_static"
350 context['unfold1_main'] = pres_view.render(self.request)
352 # XXX This is repeated in all pages
353 # more general variables expected in the template
354 context['title'] = 'Test view that combines various plugins'
355 # the menu items on the top
356 context['topmenu_items'] = topmenu_items('PresView', self.request)
357 # so we can sho who is logged
358 context['username'] = the_user(self.request)
360 prelude_env = page.prelude_env()
361 context.update(prelude_env)
365 def json_me(config_file,type):
367 for ligne in config_file:
368 if not ligne.startswith('#'):
369 args = ligne.split(';')
370 json_answer += str('{ "name": "' + args[0] + '" ,"id":"' + args[1] + '" ,"descriptif":"' + args[2]+'"')
372 json_answer += str(',"contraints":')
374 json_answer += str('""')
376 json_answer += str(args[3])
377 json_answer += str('},')
378 return json_answer[:-1]
381 DIR = '/var/myslice/'
382 STATIC = '%s/config_method_static' % DIR
383 DYNAMIC = '%s/config_method_dynamic' % DIR
384 ANIMATION = '%s/config_method_animation' % DIR
386 def pres_view_methods(request, type):
390 elif type =="static":
391 config = open(STATIC, "r")
392 json_answer = str('{ "options": [')
393 json_answer += str(json_me(config,"static"))
394 json_answer += str('] }')
396 elif type =="dynamic":
397 config = open(DYNAMIC, "r")
398 json_answer = str('{ "options": [')
399 json_answer += str(json_me(config,"dynamic"))
400 json_answer += str('] }')
402 elif type =="animation":
403 config = open(ANIMATION, "r")
404 json_answer = str('{ "options": [')
405 json_answer += str(json_me(config,"animation"))
406 json_answer += str('] }')
409 config = open(STATIC, "r")
410 json_answer = str('{ "static": [')
411 json_answer += str(json_me(config,"static"))
412 json_answer += str('],')
413 json_answer += str('"dynamic": [')
415 config = open(DYNAMIC, "r")
416 json_answer += str(json_me(config,"dynamic"))
417 json_answer += str('],')
418 json_answer += str('"animation": [')
420 config = open(ANIMATION, "r")
421 json_answer += str(json_me(config,"animation"))
422 json_answer += str('] }')
426 return HttpResponse (json_answer, mimetype="application/json")
428 def pres_view_animation(request, constraints, id):
430 # sites crees depuis 2008
431 # static.py?contraints=']date_created':1262325600&id='name_id"'
433 # method = request.getvalue('method') #ex : GetSites
434 #constraints = "']date_created':1262325600"
440 # method = 'GetSites'#request.getvalue('method') #ex : GetSites
441 # constraints = {}#request.getvalue('constraints') // nul = {}
442 # response_field = "'site_id','name','date_created'"#request.getvalue('response_field')
444 config_file = open(ANIMATION, "r")
445 for ligne in config_file:
446 if not ligne.startswith('#'):
447 ligne = ligne.split('\n')
448 first = ligne[0].split(';')
449 if (str(first[1]) == str(id)):
453 #Les print_method, print_option sont definis par le client (js)
454 #Les animations acceptent que les connexions anonymous
455 # args = "postmsg;animation;;;anonymous;https://www.planet-lab.eu/PLCAPI/;"
456 args = ";;"+str(save[8])+";"+str(save[9])+";anonymous;"+str(save[5])+";"+str(save[6])+";{"+str(constraints)+"};"+str(save[7])+";"
459 #Creation d'un objet event
463 "print_options": event.print_options,
464 "print_method": event.print_method,
465 "message": event.data
470 json_answer = json.dumps(cmd)
471 return HttpResponse (json_answer, mimetype="application/json")
473 def pres_view_static(request, constraints, id):
474 #constraints = "']date_created':1262325600"
477 # method = 'GetSites'#request.getvalue('method') #ex : GetSites
478 # constraints = {}#request.getvalue('constraints') // nul = {}
479 # response_field = "'site_id','name','date_created'"#request.getvalue('response_field')
481 config_file = open(STATIC, "r")
482 for ligne in config_file:
483 if not ligne.startswith('#'):
484 ligne = ligne.split('\n')
485 first = ligne[0].split(';')
486 if (str(first[1]) == str(id)):
490 #Les print_method, print_option sont definis par le client (js)
491 #Les animations acceptent que les connexions anonymous
492 # args = "postmsg;animation;;;anonymous;https://www.planet-lab.eu/PLCAPI/;"
493 args = ";;"+str(save[8])+";"+str(save[9])+";anonymous;"+str(save[5])+";"+str(save[6])+";{"+str(constraints)+"};"+str(save[7])+";"
496 #Creation d'un objet event
500 "print_options": event.print_options,
501 "print_method": event.print_method,
502 "message": event.data
507 json_answer = json.dumps(cmd)
508 return HttpResponse (json_answer, mimetype="application/json")
510 class ValidatePendingView(TemplateView):
511 template_name = "validate_pending.html"
513 def get_context_data(self, **kwargs):
514 # We might have slices on different registries with different user accounts
515 # 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
516 # Different registries mean different identities, unless we identify via SFA HRN or have associated the user email to a single hrn
518 #messages.info(self.request, 'You have logged in')
519 page = Page(self.request)
521 ctx_my_authorities = {}
522 ctx_delegation_authorities = {}
525 # The user need to be logged in
526 if the_user(self.request):
527 # Who can a PI validate:
528 # His own authorities + those he has credentials for.
529 # In MySlice we need to look at credentials also.
532 # XXX This will have to be asynchroneous. Need to implement barriers,
533 # for now it will be sufficient to have it working statically
535 # get user_id to later on query accounts
536 # XXX Having real query plan on local tables would simplify all this
537 # XXX $user_email is still not available for local tables
538 #user_query = Query().get('local:user').filter_by('email', '==', '$user_email').select('user_id')
539 user_query = Query().get('local:user').filter_by('email', '==', the_user(self.request)).select('user_id')
540 user, = execute_query(self.request, user_query)
541 user_id = user['user_id']
543 # Query manifold to learn about available SFA platforms for more information
544 # In general we will at least have the portal
545 # For now we are considering all registries
548 sfa_platforms_query = Query().get('local:platform').filter_by('gateway_type', '==', 'sfa').select('platform_id', 'platform', 'auth_type')
549 sfa_platforms = execute_query(self.request, sfa_platforms_query)
550 for sfa_platform in sfa_platforms:
551 print "SFA PLATFORM > ", sfa_platform['platform']
552 if not 'auth_type' in sfa_platform:
554 auth = sfa_platform['auth_type']
555 if not auth in all_authorities:
556 all_authorities.append(auth)
557 platform_ids.append(sfa_platform['platform_id'])
559 # We can check on which the user has authoritity credentials = PI rights
560 credential_authorities = set()
561 credential_authorities_expired = set()
563 # User account on these registries
564 user_accounts_query = Query.get('local:account').filter_by('user_id', '==', user_id).filter_by('platform_id', 'included', platform_ids).select('config')
565 user_accounts = execute_query(self.request, user_accounts_query)
569 for user_account in user_accounts:
570 config = json.loads(user_account['config'])
572 if 'authority_credentials' in config:
573 for authority_hrn, credential in config['authority_credentials'].items():
574 #if credential is not expired:
575 credential_authorities.add(authority_hrn)
577 # credential_authorities_expired.add(authority_hrn)
578 if 'delegated_authority_credentials' in config:
579 for authority_hrn, credential in config['delegated_authority_credentials'].items():
580 #if credential is not expired:
581 credential_authorities.add(authority_hrn)
583 # credential_authorities_expired.add(authority_hrn)
585 print 'credential_authorities =', credential_authorities
586 print 'credential_authorities_expired =', credential_authorities_expired
588 # ** Where am I a PI **
589 # For this we need to ask SFA (of all authorities) = PI function
590 pi_authorities_query = Query.get('user').filter_by('user_hrn', '==', '$user_hrn').select('pi_authorities')
591 pi_authorities_tmp = execute_query(self.request, pi_authorities_query)
592 pi_authorities = set()
593 for pa in pi_authorities_tmp:
594 pi_authorities |= set(pa['pi_authorities'])
596 print "pi_authorities =", pi_authorities
598 # My authorities + I have a credential
599 pi_credential_authorities = pi_authorities & credential_authorities
600 pi_no_credential_authorities = pi_authorities - credential_authorities - credential_authorities_expired
601 pi_expired_credential_authorities = pi_authorities & credential_authorities_expired
602 # Authorities I've been delegated PI rights
603 pi_delegation_credential_authorities = credential_authorities - pi_authorities
604 pi_delegation_expired_authorities = credential_authorities_expired - pi_authorities
606 print "pi_credential_authorities =", pi_credential_authorities
607 print "pi_no_credential_authorities =", pi_no_credential_authorities
608 print "pi_expired_credential_authorities =", pi_expired_credential_authorities
609 print "pi_delegation_credential_authorities = ", pi_delegation_credential_authorities
610 print "pi_delegation_expired_authorities = ", pi_delegation_expired_authorities
612 # Summary intermediary
613 pi_my_authorities = pi_credential_authorities | pi_no_credential_authorities | pi_expired_credential_authorities
614 pi_delegation_authorities = pi_delegation_credential_authorities | pi_delegation_expired_authorities
617 print "pi_my_authorities = ", pi_my_authorities
618 print "pi_delegation_authorities = ", pi_delegation_authorities
621 queried_pending_authorities = pi_my_authorities | pi_delegation_authorities
623 print "queried_pending_authorities = ", queried_pending_authorities
625 requests = get_request_by_authority(queried_pending_authorities)
626 for request in requests:
627 auth_hrn = request['authority_hrn']
629 if auth_hrn in pi_my_authorities:
630 dest = ctx_my_authorities
632 # define the css class
633 if auth_hrn in pi_credential_authorities:
634 request['allowed'] = 'allowed'
635 elif auth_hrn in pi_expired_credential_authorities:
636 request['allowed'] = 'expired'
637 else: # pi_no_credential_authorities
638 request['allowed'] = 'denied'
640 elif auth_hrn in pi_delegation_authorities:
641 dest = ctx_delegation_authorities
643 if auth_hrn in pi_delegation_credential_authorities:
644 request['allowed'] = 'allowed'
645 else: # pi_delegation_expired_authorities
646 request['allowed'] = 'expired'
651 if not auth_hrn in dest:
653 dest[auth_hrn].append(request)
655 context = super(ValidatePendingView, self).get_context_data(**kwargs)
656 context['my_authorities'] = ctx_my_authorities
657 context['delegation_authorities'] = ctx_delegation_authorities
659 # XXX This is repeated in all pages
660 # more general variables expected in the template
661 context['title'] = 'Test view that combines various plugins'
662 # the menu items on the top
663 context['topmenu_items'] = topmenu_items('Dashboard', self.request)
664 # so we can sho who is logged
665 context['username'] = the_user(self.request)
667 # XXX We need to prepare the page for queries
668 #context.update(page.prelude_env())