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.http import HttpResponseRedirect, HttpResponse
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 myslice.viewutils import topmenu_items, the_user
37 from plugins.lists.simplelist import SimpleList
38 from plugins.hazelnut import Hazelnut
39 from plugins.pres_view import PresView
40 from portal.event import Event
42 from portal import signals
43 from portal.forms import SliceRequestForm
44 from portal.util import RegistrationView, ActivationView
45 from portal.models import PendingUser, PendingSlice
46 from portal.actions import authority_get_pi_emails, get_request_by_authority, manifold_add_user, manifold_update_user
47 from manifold.manifoldapi import execute_query
48 from manifold.core.query import Query
49 from unfold.page import Page
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
162 class PresViewView(TemplateView):
163 template_name = "view-unfold1.html"
165 def get_context_data(self, **kwargs):
167 page = Page(self.request)
169 pres_view = PresView(page = page)
171 context = super(PresViewView, self).get_context_data(**kwargs)
173 #context['ALL_STATIC'] = "all_static"
174 context['unfold1_main'] = pres_view.render(self.request)
176 # XXX This is repeated in all pages
177 # more general variables expected in the template
178 context['title'] = 'Test view that combines various plugins'
179 # the menu items on the top
180 context['topmenu_items'] = topmenu_items('PresView', self.request)
181 # so we can sho who is logged
182 context['username'] = the_user(self.request)
184 prelude_env = page.prelude_env()
185 context.update(prelude_env)
189 def json_me(config_file,type):
191 for ligne in config_file:
192 if not ligne.startswith('#'):
193 args = ligne.split(';')
194 json_answer += str('{ "name": "' + args[0] + '" ,"id":"' + args[1] + '" ,"descriptif":"' + args[2]+'"')
196 json_answer += str(',"contraints":')
198 json_answer += str('""')
200 json_answer += str(args[3])
201 json_answer += str('},')
202 return json_answer[:-1]
205 DIR = '/var/myslice/'
206 STATIC = '%s/config_method_static' % DIR
207 DYNAMIC = '%s/config_method_dynamic' % DIR
208 ANIMATION = '%s/config_method_animation' % DIR
210 def pres_view_methods(request, type):
214 elif type =="static":
215 config = open(STATIC, "r")
216 json_answer = str('{ "options": [')
217 json_answer += str(json_me(config,"static"))
218 json_answer += str('] }')
220 elif type =="dynamic":
221 config = open(DYNAMIC, "r")
222 json_answer = str('{ "options": [')
223 json_answer += str(json_me(config,"dynamic"))
224 json_answer += str('] }')
226 elif type =="animation":
227 config = open(ANIMATION, "r")
228 json_answer = str('{ "options": [')
229 json_answer += str(json_me(config,"animation"))
230 json_answer += str('] }')
233 config = open(STATIC, "r")
234 json_answer = str('{ "static": [')
235 json_answer += str(json_me(config,"static"))
236 json_answer += str('],')
237 json_answer += str('"dynamic": [')
239 config = open(DYNAMIC, "r")
240 json_answer += str(json_me(config,"dynamic"))
241 json_answer += str('],')
242 json_answer += str('"animation": [')
244 config = open(ANIMATION, "r")
245 json_answer += str(json_me(config,"animation"))
246 json_answer += str('] }')
250 return HttpResponse (json_answer, mimetype="application/json")
252 def pres_view_animation(request, constraints, id):
254 # sites crees depuis 2008
255 # static.py?contraints=']date_created':1262325600&id='name_id"'
257 # method = request.getvalue('method') #ex : GetSites
258 #constraints = "']date_created':1262325600"
264 # method = 'GetSites'#request.getvalue('method') #ex : GetSites
265 # constraints = {}#request.getvalue('constraints') // nul = {}
266 # response_field = "'site_id','name','date_created'"#request.getvalue('response_field')
268 config_file = open(ANIMATION, "r")
269 for ligne in config_file:
270 if not ligne.startswith('#'):
271 ligne = ligne.split('\n')
272 first = ligne[0].split(';')
273 if (str(first[1]) == str(id)):
277 #Les print_method, print_option sont definis par le client (js)
278 #Les animations acceptent que les connexions anonymous
279 # args = "postmsg;animation;;;anonymous;https://www.planet-lab.eu/PLCAPI/;"
280 args = ";;"+str(save[8])+";"+str(save[9])+";anonymous;"+str(save[5])+";"+str(save[6])+";{"+str(constraints)+"};"+str(save[7])+";"
283 #Creation d'un objet event
287 "print_options": event.print_options,
288 "print_method": event.print_method,
289 "message": event.data
294 json_answer = json.dumps(cmd)
295 return HttpResponse (json_answer, mimetype="application/json")
297 def pres_view_static(request, constraints, id):
298 #constraints = "']date_created':1262325600"
301 # method = 'GetSites'#request.getvalue('method') #ex : GetSites
302 # constraints = {}#request.getvalue('constraints') // nul = {}
303 # response_field = "'site_id','name','date_created'"#request.getvalue('response_field')
305 config_file = open(STATIC, "r")
306 for ligne in config_file:
307 if not ligne.startswith('#'):
308 ligne = ligne.split('\n')
309 first = ligne[0].split(';')
310 if (str(first[1]) == str(id)):
314 #Les print_method, print_option sont definis par le client (js)
315 #Les animations acceptent que les connexions anonymous
316 # args = "postmsg;animation;;;anonymous;https://www.planet-lab.eu/PLCAPI/;"
317 args = ";;"+str(save[8])+";"+str(save[9])+";anonymous;"+str(save[5])+";"+str(save[6])+";{"+str(constraints)+"};"+str(save[7])+";"
320 #Creation d'un objet event
324 "print_options": event.print_options,
325 "print_method": event.print_method,
326 "message": event.data
331 json_answer = json.dumps(cmd)
332 return HttpResponse (json_answer, mimetype="application/json")
334 class ValidatePendingView(TemplateView):
335 template_name = "validate_pending.html"
337 def get_context_data(self, **kwargs):
338 # We might have slices on different registries with different user accounts
339 # 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
340 # Different registries mean different identities, unless we identify via SFA HRN or have associated the user email to a single hrn
342 #messages.info(self.request, 'You have logged in')
343 page = Page(self.request)
345 ctx_my_authorities = {}
346 ctx_delegation_authorities = {}
349 # The user need to be logged in
350 if the_user(self.request):
351 # Who can a PI validate:
352 # His own authorities + those he has credentials for.
353 # In MySlice we need to look at credentials also.
356 # XXX This will have to be asynchroneous. Need to implement barriers,
357 # for now it will be sufficient to have it working statically
359 # get user_id to later on query accounts
360 # XXX Having real query plan on local tables would simplify all this
361 # XXX $user_email is still not available for local tables
362 #user_query = Query().get('local:user').filter_by('email', '==', '$user_email').select('user_id')
363 user_query = Query().get('local:user').filter_by('email', '==', the_user(self.request)).select('user_id')
364 user, = execute_query(self.request, user_query)
365 user_id = user['user_id']
367 # Query manifold to learn about available SFA platforms for more information
368 # In general we will at least have the portal
369 # For now we are considering all registries
372 sfa_platforms_query = Query().get('local:platform').filter_by('gateway_type', '==', 'sfa').select('platform_id', 'platform', 'auth_type')
373 sfa_platforms = execute_query(self.request, sfa_platforms_query)
374 for sfa_platform in sfa_platforms:
375 print "SFA PLATFORM > ", sfa_platform['platform']
376 if not 'auth_type' in sfa_platform:
378 auth = sfa_platform['auth_type']
379 if not auth in all_authorities:
380 all_authorities.append(auth)
381 platform_ids.append(sfa_platform['platform_id'])
383 # We can check on which the user has authoritity credentials = PI rights
384 credential_authorities = set()
385 credential_authorities_expired = set()
387 # User account on these registries
388 user_accounts_query = Query.get('local:account').filter_by('user_id', '==', user_id).filter_by('platform_id', 'included', platform_ids).select('config')
389 user_accounts = execute_query(self.request, user_accounts_query)
393 for user_account in user_accounts:
394 config = json.loads(user_account['config'])
396 if 'authority_credentials' in config:
397 for authority_hrn, credential in config['authority_credentials'].items():
398 #if credential is not expired:
399 credential_authorities.add(authority_hrn)
401 # credential_authorities_expired.add(authority_hrn)
402 if 'delegated_authority_credentials' in config:
403 for authority_hrn, credential in config['delegated_authority_credentials'].items():
404 #if credential is not expired:
405 credential_authorities.add(authority_hrn)
407 # credential_authorities_expired.add(authority_hrn)
409 print 'credential_authorities =', credential_authorities
410 print 'credential_authorities_expired =', credential_authorities_expired
412 # ** Where am I a PI **
413 # For this we need to ask SFA (of all authorities) = PI function
414 pi_authorities_query = Query.get('user').filter_by('user_hrn', '==', '$user_hrn').select('pi_authorities')
415 pi_authorities_tmp = execute_query(self.request, pi_authorities_query)
416 pi_authorities = set()
417 for pa in pi_authorities_tmp:
418 pi_authorities |= set(pa['pi_authorities'])
420 print "pi_authorities =", pi_authorities
422 # My authorities + I have a credential
423 pi_credential_authorities = pi_authorities & credential_authorities
424 pi_no_credential_authorities = pi_authorities - credential_authorities - credential_authorities_expired
425 pi_expired_credential_authorities = pi_authorities & credential_authorities_expired
426 # Authorities I've been delegated PI rights
427 pi_delegation_credential_authorities = credential_authorities - pi_authorities
428 pi_delegation_expired_authorities = credential_authorities_expired - pi_authorities
430 print "pi_credential_authorities =", pi_credential_authorities
431 print "pi_no_credential_authorities =", pi_no_credential_authorities
432 print "pi_expired_credential_authorities =", pi_expired_credential_authorities
433 print "pi_delegation_credential_authorities = ", pi_delegation_credential_authorities
434 print "pi_delegation_expired_authorities = ", pi_delegation_expired_authorities
436 # Summary intermediary
437 pi_my_authorities = pi_credential_authorities | pi_no_credential_authorities | pi_expired_credential_authorities
438 pi_delegation_authorities = pi_delegation_credential_authorities | pi_delegation_expired_authorities
441 print "pi_my_authorities = ", pi_my_authorities
442 print "pi_delegation_authorities = ", pi_delegation_authorities
445 queried_pending_authorities = pi_my_authorities | pi_delegation_authorities
447 print "queried_pending_authorities = ", queried_pending_authorities
449 requests = get_request_by_authority(queried_pending_authorities)
450 for request in requests:
451 auth_hrn = request['authority_hrn']
453 if auth_hrn in pi_my_authorities:
454 dest = ctx_my_authorities
456 # define the css class
457 if auth_hrn in pi_credential_authorities:
458 request['allowed'] = 'allowed'
459 elif auth_hrn in pi_expired_credential_authorities:
460 request['allowed'] = 'expired'
461 else: # pi_no_credential_authorities
462 request['allowed'] = 'denied'
464 elif auth_hrn in pi_delegation_authorities:
465 dest = ctx_delegation_authorities
467 if auth_hrn in pi_delegation_credential_authorities:
468 request['allowed'] = 'allowed'
469 else: # pi_delegation_expired_authorities
470 request['allowed'] = 'expired'
475 if not auth_hrn in dest:
477 dest[auth_hrn].append(request)
479 context = super(ValidatePendingView, self).get_context_data(**kwargs)
480 context['my_authorities'] = ctx_my_authorities
481 context['delegation_authorities'] = ctx_delegation_authorities
483 # XXX This is repeated in all pages
484 # more general variables expected in the template
485 context['title'] = 'Test view that combines various plugins'
486 # the menu items on the top
487 context['topmenu_items'] = topmenu_items('Dashboard', self.request)
488 # so we can sho who is logged
489 context['username'] = the_user(self.request)
491 # XXX We need to prepare the page for queries
492 #context.update(page.prelude_env())