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
52 #my_acc form value processing
53 def account_process(request):
54 user_query = Query().get('local:user').select('password','config')
55 user_details = execute_query(request, user_query)
57 if 'submit_name' in request.POST:
58 edited_first_name = request.POST['fname']
59 edited_last_name = request.POST['lname']
62 for user_config in user_details:
63 #email = user_detail['email']
64 if user_config['config']:
65 config = json.loads(user_config['config'])
66 config['firstname'] = edited_first_name
67 config['lastname'] = edited_last_name
68 config['authority'] = config.get('authority','Unknown Authority')
69 updated_config = json.dumps(config)
71 # updating config local:user in manifold
72 user_params = { 'config': updated_config}
73 manifold_update_user(request,user_params)
74 # this will be depricated, we will show the success msg in same page
75 return HttpResponse('Sucess: First Name and Last Name Updated!')
76 elif 'submit_pass' in request.POST:
77 edited_password = request.POST['password']
79 for user_pass in user_details:
80 user_pass['password'] = edited_password
81 #updating password in local:user
82 user_params = { 'password': user_pass['password']}
83 manifold_update_user(request,user_params)
85 return HttpResponse('Success: Password Changed!!')
86 elif 'generate' in request.POST:
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())
100 get_user.keypair = keypair
102 return HttpResponse('Success: New Keypair Generated! %s' % keypair)
104 elif 'upload_key' in request.POST:
105 up_file = request.FILES['pubkey']
106 file_content = up_file.read()
107 file_name = up_file.name
108 file_extension = os.path.splitext(file_name)[1]
109 allowed_extension = ['.pub','.txt']
110 if file_extension in allowed_extension and re.search(r'ssh-rsa',file_content):
111 file_content = '{"user_public_key":"'+ file_content +'"}'
112 file_content = re.sub("\r", "", file_content)
113 file_content = re.sub("\n", "\\n",file_content)
114 file_content = ''.join(file_content.split())
115 get_user.keypair = file_content
117 return HttpResponse('Success: Publickey uploaded! Old records overwritten')
119 return HttpResponse('Please upload a valid RSA public key [.txt or .pub].')
122 message = 'You submitted an empty form.'
123 return HttpResponse(message)
125 def register_4m_f4f(request):
128 authorities_query = Query.get('authority').filter_by('authority_hrn', 'included', ['ple.inria', 'ple.upmc']).select('name', 'authority_hrn')
129 #authorities_query = Query.get('authority').select('authority_hrn')
130 authorities = execute_query(request, authorities_query)
132 if request.method == 'POST':
133 # We shall use a form here
135 #get_email = PendingUser.objects.get(email)
136 reg_fname = request.POST.get('firstname', '')
137 reg_lname = request.POST.get('lastname', '')
138 #reg_aff = request.POST.get('affiliation','')
139 reg_auth = request.POST.get('authority_hrn', '')
140 reg_email = request.POST.get('email','').lower()
142 #POST value validation
143 if (re.search(r'^[\w+\s.@+-]+$', reg_fname)==None):
144 errors.append('First Name may contain only letters, numbers, spaces and @/./+/-/_ characters.')
145 #return HttpResponse("Only Letters, Numbers, - and _ allowd in First Name")
146 #return render(request, 'register_4m_f4f.html')
147 if (re.search(r'^[\w+\s.@+-]+$', reg_lname) == None):
148 errors.append('Last Name may contain only letters, numbers, spaces and @/./+/-/_ characters.')
149 #return HttpResponse("Only Letters, Numbers, - and _ is allowed in Last name")
150 #return render(request, 'register_4m_f4f.html')
151 # if (re.search(r'^[\w+\s.@+-]+$', reg_aff) == None):
152 # errors.append('Affiliation may contain only letters, numbers, spaces and @/./+/-/_ characters.')
153 #return HttpResponse("Only Letters, Numbers and _ is allowed in Affiliation")
154 #return render(request, 'register_4m_f4f.html')
155 # XXX validate authority hrn !!
156 if PendingUser.objects.filter(email__iexact=reg_email):
157 errors.append('Email already registered.Please provide a new email address.')
158 #return HttpResponse("Email Already exists")
159 #return render(request, 'register_4m_f4f.html')
160 if 'generate' in request.POST['question']:
161 # Generate public and private keys using SFA Library
162 from sfa.trust.certificate import Keypair
163 k = Keypair(create=True)
164 public_key = k.get_pubkey_string()
165 private_key = k.as_pem()
166 private_key = ''.join(private_key.split())
167 public_key = "ssh-rsa " + public_key
169 keypair = '{"user_public_key":"'+ public_key + '", "user_private_key":"'+ private_key + '"}'
170 # keypair = re.sub("\r", "", keypair)
171 # keypair = re.sub("\n", "\\n", keypair)
172 # #keypair = keypair.rstrip('\r\n')
173 # keypair = ''.join(keypair.split())
175 up_file = request.FILES['user_public_key']
176 file_content = up_file.read()
177 file_name = up_file.name
178 file_extension = os.path.splitext(file_name)[1]
179 allowed_extension = ['.pub','.txt']
180 if file_extension in allowed_extension and re.search(r'ssh-rsa',file_content):
181 keypair = '{"user_public_key":"'+ file_content +'"}'
182 keypair = re.sub("\r", "", keypair)
183 keypair = re.sub("\n", "\\n",keypair)
184 keypair = ''.join(keypair.split())
186 errors.append('Please upload a valid RSA public key [.txt or .pub].')
188 #b = PendingUser(first_name=reg_fname, last_name=reg_lname, affiliation=reg_aff,
189 # email=reg_email, password=request.POST['password'], keypair=keypair)
193 first_name=reg_fname,
195 #affiliation=reg_aff,
196 authority_hrn=reg_auth,
198 password=request.POST['password'],
205 first_name : reg_fname,
206 last_name : reg_lname,
207 #affiliation : reg_aff,
208 authority_hrn: reg_auth,
211 cc_myself : True # form.cleaned_data['cc_myself']
214 recipients = authority_get_pi_emails(authority_hrn)
216 recipients.append(ctx['email'])
218 msg = render_to_string('user_request_email.txt', ctx)
219 send_mail("Onelab New User request submitted", msg, email, recipients)
221 return render(request, 'user_register_complete.html')
223 return render(request, 'register_4m_f4f.html',{
224 'topmenu_items': topmenu_items('Register', request),
226 'firstname': request.POST.get('firstname', ''),
227 'lastname': request.POST.get('lastname', ''),
228 #'affiliation': request.POST.get('affiliation', ''),
229 'authority_hrn': request.POST.get('authority_hrn', ''),
230 'email': request.POST.get('email', ''),
231 'password': request.POST.get('password', ''),
232 'authorities': authorities
237 def slice_request(request):
240 authorities_query = Query.get('authority').filter_by('authority_hrn', 'included', ['ple.inria', 'ple.upmc']).select('name', 'authority_hrn')
241 #authorities_query = Query.get('authority').select('authority_hrn')
242 authorities = execute_query(request, authorities_query)
244 authority_hrn_tuple = []
245 for authority in authorities:
246 authority_hrn_tuple.append((authority['authority_hrn'], authority['name']))
247 authority_hrn_initial = {'authority_hrn': authority_hrn_tuple}
249 # request.POST or None ?
250 if request.method == 'POST':
251 # The form has been submitted
252 form = SliceRequestForm(request.POST, initial=authority_hrn_initial)
255 slice_name = form.cleaned_data['slice_name']
256 authority_hrn = form.cleaned_data['authority_hrn']
257 number_of_nodes = form.cleaned_data['number_of_nodes']
258 type_of_nodes = form.cleaned_data['type_of_nodes']
259 purpose = form.cleaned_data['purpose']
262 slice_name = slice_name,
263 authority_hrn = authority_hrn,
264 number_of_nodes = number_of_nodes,
265 type_of_nodes = type_of_nodes,
270 # All validation rules pass; process data in form.cleaned_data
271 # slice_name, number_of_nodes, type_of_nodes, purpose
272 email = form.cleaned_data['email'] # email of the sender
273 cc_myself = form.cleaned_data['cc_myself']
275 # The recipients are the PI of the authority
276 recipients = authority_get_pi_emails(authority_hrn)
277 #recipients = ['yasin.upmc@gmail.com','jordan.auge@lip6.fr']
279 recipients.append(email)
280 msg = render_to_string('slice_request_email.txt', form.cleaned_data)
281 send_mail("Onelab New Slice request form submitted", msg, email, recipients)
283 return render(request,'slicereq_recvd.html') # Redirect after POST
285 form = SliceRequestForm(initial=authority_hrn_initial)
288 # template_env['form'] = form
289 # template_env['topmenu_items'] = topmenu_items('Request a slice', request)
290 # template_env['unfold1_main'] = render(request, 'slice_request_.html', {
293 # from django.shortcuts import render_to_response
294 # from django.template import RequestContext
295 # return render_to_response ('view-unfold1.html',template_env,
296 # context_instance=RequestContext(request))
298 return render(request, 'slice_request.html', {
300 'topmenu_items': topmenu_items('Request a slice', request),
301 'username': the_user (request)
305 class PresViewView(TemplateView):
306 template_name = "view-unfold1.html"
308 def get_context_data(self, **kwargs):
310 page = Page(self.request)
312 pres_view = PresView(page = page)
314 context = super(PresViewView, self).get_context_data(**kwargs)
316 #context['ALL_STATIC'] = "all_static"
317 context['unfold1_main'] = pres_view.render(self.request)
319 # XXX This is repeated in all pages
320 # more general variables expected in the template
321 context['title'] = 'Test view that combines various plugins'
322 # the menu items on the top
323 context['topmenu_items'] = topmenu_items('PresView', self.request)
324 # so we can sho who is logged
325 context['username'] = the_user(self.request)
327 prelude_env = page.prelude_env()
328 context.update(prelude_env)
332 def json_me(config_file,type):
334 for ligne in config_file:
335 if not ligne.startswith('#'):
336 args = ligne.split(';')
337 json_answer += str('{ "name": "' + args[0] + '" ,"id":"' + args[1] + '" ,"descriptif":"' + args[2]+'"')
339 json_answer += str(',"contraints":')
341 json_answer += str('""')
343 json_answer += str(args[3])
344 json_answer += str('},')
345 return json_answer[:-1]
348 DIR = '/var/myslice/'
349 STATIC = '%s/config_method_static' % DIR
350 DYNAMIC = '%s/config_method_dynamic' % DIR
351 ANIMATION = '%s/config_method_animation' % DIR
353 def pres_view_methods(request, type):
357 elif type =="static":
358 config = open(STATIC, "r")
359 json_answer = str('{ "options": [')
360 json_answer += str(json_me(config,"static"))
361 json_answer += str('] }')
363 elif type =="dynamic":
364 config = open(DYNAMIC, "r")
365 json_answer = str('{ "options": [')
366 json_answer += str(json_me(config,"dynamic"))
367 json_answer += str('] }')
369 elif type =="animation":
370 config = open(ANIMATION, "r")
371 json_answer = str('{ "options": [')
372 json_answer += str(json_me(config,"animation"))
373 json_answer += str('] }')
376 config = open(STATIC, "r")
377 json_answer = str('{ "static": [')
378 json_answer += str(json_me(config,"static"))
379 json_answer += str('],')
380 json_answer += str('"dynamic": [')
382 config = open(DYNAMIC, "r")
383 json_answer += str(json_me(config,"dynamic"))
384 json_answer += str('],')
385 json_answer += str('"animation": [')
387 config = open(ANIMATION, "r")
388 json_answer += str(json_me(config,"animation"))
389 json_answer += str('] }')
393 return HttpResponse (json_answer, mimetype="application/json")
395 def pres_view_animation(request, constraints, id):
397 # sites crees depuis 2008
398 # static.py?contraints=']date_created':1262325600&id='name_id"'
400 # method = request.getvalue('method') #ex : GetSites
401 #constraints = "']date_created':1262325600"
407 # method = 'GetSites'#request.getvalue('method') #ex : GetSites
408 # constraints = {}#request.getvalue('constraints') // nul = {}
409 # response_field = "'site_id','name','date_created'"#request.getvalue('response_field')
411 config_file = open(ANIMATION, "r")
412 for ligne in config_file:
413 if not ligne.startswith('#'):
414 ligne = ligne.split('\n')
415 first = ligne[0].split(';')
416 if (str(first[1]) == str(id)):
420 #Les print_method, print_option sont definis par le client (js)
421 #Les animations acceptent que les connexions anonymous
422 # args = "postmsg;animation;;;anonymous;https://www.planet-lab.eu/PLCAPI/;"
423 args = ";;"+str(save[8])+";"+str(save[9])+";anonymous;"+str(save[5])+";"+str(save[6])+";{"+str(constraints)+"};"+str(save[7])+";"
426 #Creation d'un objet event
430 "print_options": event.print_options,
431 "print_method": event.print_method,
432 "message": event.data
437 json_answer = json.dumps(cmd)
438 return HttpResponse (json_answer, mimetype="application/json")
440 def pres_view_static(request, constraints, id):
441 #constraints = "']date_created':1262325600"
444 # method = 'GetSites'#request.getvalue('method') #ex : GetSites
445 # constraints = {}#request.getvalue('constraints') // nul = {}
446 # response_field = "'site_id','name','date_created'"#request.getvalue('response_field')
448 config_file = open(STATIC, "r")
449 for ligne in config_file:
450 if not ligne.startswith('#'):
451 ligne = ligne.split('\n')
452 first = ligne[0].split(';')
453 if (str(first[1]) == str(id)):
457 #Les print_method, print_option sont definis par le client (js)
458 #Les animations acceptent que les connexions anonymous
459 # args = "postmsg;animation;;;anonymous;https://www.planet-lab.eu/PLCAPI/;"
460 args = ";;"+str(save[8])+";"+str(save[9])+";anonymous;"+str(save[5])+";"+str(save[6])+";{"+str(constraints)+"};"+str(save[7])+";"
463 #Creation d'un objet event
467 "print_options": event.print_options,
468 "print_method": event.print_method,
469 "message": event.data
474 json_answer = json.dumps(cmd)
475 return HttpResponse (json_answer, mimetype="application/json")
477 class ValidatePendingView(TemplateView):
478 template_name = "validate_pending.html"
480 def get_context_data(self, **kwargs):
481 # We might have slices on different registries with different user accounts
482 # 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
483 # Different registries mean different identities, unless we identify via SFA HRN or have associated the user email to a single hrn
485 #messages.info(self.request, 'You have logged in')
486 page = Page(self.request)
488 ctx_my_authorities = {}
489 ctx_delegation_authorities = {}
492 # The user need to be logged in
493 if the_user(self.request):
494 # Who can a PI validate:
495 # His own authorities + those he has credentials for.
496 # In MySlice we need to look at credentials also.
499 # XXX This will have to be asynchroneous. Need to implement barriers,
500 # for now it will be sufficient to have it working statically
502 # get user_id to later on query accounts
503 # XXX Having real query plan on local tables would simplify all this
504 # XXX $user_email is still not available for local tables
505 #user_query = Query().get('local:user').filter_by('email', '==', '$user_email').select('user_id')
506 user_query = Query().get('local:user').filter_by('email', '==', the_user(self.request)).select('user_id')
507 user, = execute_query(self.request, user_query)
508 user_id = user['user_id']
510 # Query manifold to learn about available SFA platforms for more information
511 # In general we will at least have the portal
512 # For now we are considering all registries
515 sfa_platforms_query = Query().get('local:platform').filter_by('gateway_type', '==', 'sfa').select('platform_id', 'platform', 'auth_type')
516 sfa_platforms = execute_query(self.request, sfa_platforms_query)
517 for sfa_platform in sfa_platforms:
518 print "SFA PLATFORM > ", sfa_platform['platform']
519 if not 'auth_type' in sfa_platform:
521 auth = sfa_platform['auth_type']
522 if not auth in all_authorities:
523 all_authorities.append(auth)
524 platform_ids.append(sfa_platform['platform_id'])
526 # We can check on which the user has authoritity credentials = PI rights
527 credential_authorities = set()
528 credential_authorities_expired = set()
530 # User account on these registries
531 user_accounts_query = Query.get('local:account').filter_by('user_id', '==', user_id).filter_by('platform_id', 'included', platform_ids).select('config')
532 user_accounts = execute_query(self.request, user_accounts_query)
536 for user_account in user_accounts:
537 config = json.loads(user_account['config'])
539 if 'authority_credentials' in config:
540 for authority_hrn, credential in config['authority_credentials'].items():
541 #if credential is not expired:
542 credential_authorities.add(authority_hrn)
544 # credential_authorities_expired.add(authority_hrn)
545 if 'delegated_authority_credentials' in config:
546 for authority_hrn, credential in config['delegated_authority_credentials'].items():
547 #if credential is not expired:
548 credential_authorities.add(authority_hrn)
550 # credential_authorities_expired.add(authority_hrn)
552 print 'credential_authorities =', credential_authorities
553 print 'credential_authorities_expired =', credential_authorities_expired
555 # ** Where am I a PI **
556 # For this we need to ask SFA (of all authorities) = PI function
557 pi_authorities_query = Query.get('user').filter_by('user_hrn', '==', '$user_hrn').select('pi_authorities')
558 pi_authorities_tmp = execute_query(self.request, pi_authorities_query)
559 pi_authorities = set()
560 for pa in pi_authorities_tmp:
561 pi_authorities |= set(pa['pi_authorities'])
563 print "pi_authorities =", pi_authorities
565 # My authorities + I have a credential
566 pi_credential_authorities = pi_authorities & credential_authorities
567 pi_no_credential_authorities = pi_authorities - credential_authorities - credential_authorities_expired
568 pi_expired_credential_authorities = pi_authorities & credential_authorities_expired
569 # Authorities I've been delegated PI rights
570 pi_delegation_credential_authorities = credential_authorities - pi_authorities
571 pi_delegation_expired_authorities = credential_authorities_expired - pi_authorities
573 print "pi_credential_authorities =", pi_credential_authorities
574 print "pi_no_credential_authorities =", pi_no_credential_authorities
575 print "pi_expired_credential_authorities =", pi_expired_credential_authorities
576 print "pi_delegation_credential_authorities = ", pi_delegation_credential_authorities
577 print "pi_delegation_expired_authorities = ", pi_delegation_expired_authorities
579 # Summary intermediary
580 pi_my_authorities = pi_credential_authorities | pi_no_credential_authorities | pi_expired_credential_authorities
581 pi_delegation_authorities = pi_delegation_credential_authorities | pi_delegation_expired_authorities
584 print "pi_my_authorities = ", pi_my_authorities
585 print "pi_delegation_authorities = ", pi_delegation_authorities
588 queried_pending_authorities = pi_my_authorities | pi_delegation_authorities
590 print "queried_pending_authorities = ", queried_pending_authorities
592 requests = get_request_by_authority(queried_pending_authorities)
593 for request in requests:
594 auth_hrn = request['authority_hrn']
596 if auth_hrn in pi_my_authorities:
597 dest = ctx_my_authorities
599 # define the css class
600 if auth_hrn in pi_credential_authorities:
601 request['allowed'] = 'allowed'
602 elif auth_hrn in pi_expired_credential_authorities:
603 request['allowed'] = 'expired'
604 else: # pi_no_credential_authorities
605 request['allowed'] = 'denied'
607 elif auth_hrn in pi_delegation_authorities:
608 dest = ctx_delegation_authorities
610 if auth_hrn in pi_delegation_credential_authorities:
611 request['allowed'] = 'allowed'
612 else: # pi_delegation_expired_authorities
613 request['allowed'] = 'expired'
618 if not auth_hrn in dest:
620 dest[auth_hrn].append(request)
622 context = super(ValidatePendingView, self).get_context_data(**kwargs)
623 context['my_authorities'] = ctx_my_authorities
624 context['delegation_authorities'] = ctx_delegation_authorities
626 # XXX This is repeated in all pages
627 # more general variables expected in the template
628 context['title'] = 'Test view that combines various plugins'
629 # the menu items on the top
630 context['topmenu_items'] = topmenu_items('Dashboard', self.request)
631 # so we can sho who is logged
632 context['username'] = the_user(self.request)
634 # XXX We need to prepare the page for queries
635 #context.update(page.prelude_env())