portal: added wip for PI validation page
[myslice.git] / portal / views.py
1 # -*- coding: utf-8 -*-
2 #
3 # portal/views.py: views for the portal application
4 # This file is part of the Manifold project.
5 #
6 # Authors:
7 #   Jordan AugĂ© <jordan.auge@lip6.fr>
8 #   Mohammed Yasin Rahman <mohammed-yasin.rahman@lip6.fr>
9 # Copyright 2013, UPMC Sorbonne UniversitĂ©s / LIP6
10 #
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.
14
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
18 # details.
19
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.
23
24 from django.conf                 import settings
25 from django.contrib.sites.models import Site, RequestSite
26 from django.contrib              import messages
27 from django.views.generic        import View
28 from django.views.generic.base   import TemplateView
29 from django.shortcuts            import render
30
31 from plugins.lists.simplelist    import SimpleList
32
33 from plugins.pres_view           import PresView
34 from portal.event import Event
35 import json
36
37 from portal                      import signals
38 from portal.forms                import UserRegisterForm, SliceRequestForm, ContactForm
39 from portal.util                 import RegistrationView, ActivationView
40 from portal.models               import PendingUser, PendingSlice
41 from manifold.core.query         import Query
42 from manifold.manifoldapi        import execute_query
43 from unfold.page                 import Page
44 from myslice.viewutils           import topmenu_items, the_user
45 from django.http                 import HttpResponseRedirect, HttpResponse
46
47 from M2Crypto                    import Rand, RSA, BIO
48 import os, re
49
50 class DashboardView(TemplateView):
51     template_name = "dashboard.html"
52
53     def get_context_data(self, **kwargs):
54         # We might have slices on different registries with different user accounts 
55         # 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
56         # Different registries mean different identities, unless we identify via SFA HRN or have associated the user email to a single hrn
57
58         #messages.info(self.request, 'You have logged in')
59         page = Page(self.request)
60
61         # Slow...
62         #slice_query = Query().get('slice').filter_by('user.user_hrn', 'contains', user_hrn).select('slice_hrn')
63         slice_query = Query().get('user').filter_by('user_hrn', '==', '$user_hrn').select('user_hrn', 'slice.slice_hrn')
64         auth_query  = Query().get('network').select('network_hrn')
65         print "AUTH QUERY =====================", auth_query
66         print "filter", auth_query.filters
67         page.enqueue_query(slice_query)
68         page.enqueue_query(auth_query)
69
70         page.expose_js_metadata()
71         page.expose_queries()
72
73         slicelist = SimpleList(
74             title = None,
75             page  = page,
76             key   = 'slice.slice_hrn',
77             query = slice_query,
78         )
79          
80         authlist = SimpleList(
81             title = None,
82             page  = page,
83             key   = 'network_hrn',
84             query = auth_query,
85         )
86
87         context = super(DashboardView, self).get_context_data(**kwargs)
88         context['person']   = self.request.user
89         context['networks'] = authlist.render(self.request) 
90         context['slices']   = slicelist.render(self.request)
91
92         # XXX This is repeated in all pages
93         # more general variables expected in the template
94         context['title'] = 'Test view that combines various plugins'
95         # the menu items on the top
96         context['topmenu_items'] = topmenu_items('Dashboard', self.request) 
97         # so we can sho who is logged
98         context['username'] = the_user(self.request) 
99
100         context.update(page.prelude_env())
101
102         return context
103
104 class UserRegisterView(RegistrationView):
105     """
106     A registration backend which follows a simple workflow:
107
108     1. User signs up, inactive account is created.
109
110     2. Email is sent to user with activation link.
111
112     3. User clicks activation link, account is now active.
113
114     Using this backend requires that
115
116     * ``registration`` be listed in the ``INSTALLED_APPS`` setting
117       (since this backend makes use of models defined in this
118       application).
119
120     * The setting ``ACCOUNT_ACTIVATION_DAYS`` be supplied, specifying
121       (as an integer) the number of days from registration during
122       which a user may activate their account (after that period
123       expires, activation will be disallowed).
124
125     * The creation of the templates
126       ``registration/activation_email_subject.txt`` and
127       ``registration/activation_email.txt``, which will be used for
128       the activation email. See the notes for this backends
129       ``register`` method for details regarding these templates.
130
131     Additionally, registration can be temporarily closed by adding the
132     setting ``REGISTRATION_OPEN`` and setting it to
133     ``False``. Omitting this setting, or setting it to ``True``, will
134     be interpreted as meaning that registration is currently open and
135     permitt ed.
136
137     Internally, this is accomplished via storing an activation key in
138     an instance of ``registration.models.RegistrationProfile``. See
139     that model and its custom manager for full documentation of its
140     fields and supported operations.
141     
142     """
143     form_class = UserRegisterForm
144     
145     def register(self, request, **cleaned_data):
146         """
147         Given a username, email address and password, register a new
148         user account, which will initially be inactive.
149
150         Along with the new ``User`` object, a new
151         ``registration.models.RegistrationProfile`` will be created,
152         tied to that ``User``, containing the activation key which
153         will be used for this account.
154
155         An email will be sent to the supplied email address; this
156         email should contain an activation link. The email will be
157         rendered using two templates. See the documentation for
158         ``RegistrationProfile.send_activation_email()`` for
159         information about these templates and the contexts provided to
160         them.
161
162         After the ``User`` and ``RegistrationProfile`` are created and
163         the activation email is sent, the signal
164         ``registration.signals.user_registered`` will be sent, with
165         the new ``User`` as the keyword argument ``user`` and the
166         class of this backend as the sender.
167
168         """
169         first_name = cleaned_data['first_name']
170         last_name  = cleaned_data['last_name']
171         affiliation= cleaned_data['affiliation']
172         email      = cleaned_data['email']
173         password   = cleaned_data['password1']
174         
175         #password2  = cleaned_data['password2']
176         keypair    = cleaned_data['keypair']
177
178         #if Site._meta.installed:
179         #    site = Site.objects.get_current()
180         #else:
181         #    site = RequestSite(request) 
182         site = None
183
184         new_user = PendingUser.objects.create_inactive_user(first_name, last_name, email, password, site)
185         signals.user_registered.send(sender=self.__class__,
186                                      user=new_user,
187                                      request=request)
188         return new_user
189
190     def get_context_data(self, **kwargs):
191         context = super(UserRegisterView, self).get_context_data(**kwargs)
192         context['topmenu_items'] = topmenu_items('Register', self.request)
193         context['username'] = the_user (self.request)
194         return context
195
196     def registration_allowed(self, request):
197         """
198         Indicate whether account registration is currently permitted,
199         based on the value of the setting ``REGISTRATION_OPEN``. This
200         is determined as follows:
201
202         * If ``REGISTRATION_OPEN`` is not specified in settings, or is
203           set to ``True``, registration is permitted.
204
205         * If ``REGISTRATION_OPEN`` is both specified and set to
206           ``False``, registration is not permitted.
207         
208         """
209         return getattr(settings, 'REGISTRATION_OPEN', True)
210
211     def get_success_url(self, request, user):
212         """
213         Return the name of the URL to redirect to after successful
214         user registration.
215         
216         """
217         return ('user_register_complete', (), {})
218
219
220 class UserValidateView(ActivationView):
221     def activate(self, request, activation_key):
222         """
223         Given an an activation key, look up and activate the user
224         account corresponding to that key (if possible).
225
226         After successful activation, the signal
227         ``registration.signals.user_activated`` will be sent, with the
228         newly activated ``User`` as the keyword argument ``user`` and
229         the class of this backend as the sender.
230         
231         """
232         activated_user = RegistrationProfile.objects.activate_user(activation_key)
233         if activated_user:
234             signals.user_activated.send(sender=self.__class__,
235                                         user=activated_user,
236                                         request=request)
237         return activated_user
238
239     def get_success_url(self, request, user):
240         return ('registration_activation_complete', (), {})
241
242
243 # DEPRECATED #from portal.portalpage  import PortalPage
244 # DEPRECATED #from plugins.wizard     import Wizard
245 # DEPRECATED #from plugins.form       import CreateForm
246 # DEPRECATED #from plugins.raw.raw    import Raw          # XXX
247 # DEPRECATED #
248 # DEPRECATED #from myslice.viewutils  import the_user
249 # DEPRECATED #
250 # DEPRECATED #from django.template.loader import render_to_string
251 # DEPRECATED #from django.template import RequestContext
252 # DEPRECATED #from django.views import generic
253 # DEPRECATED #
254 # DEPRECATED #from django.contrib.formtools.wizard.views import NamedUrlSessionWizardView
255 # DEPRECATED ##from django.core.files.storage import FileSystemStorage
256 # DEPRECATED #from django.core.files.storage import default_storage
257 # DEPRECATED #
258 # DEPRECATED ##class MerlinWizard(NamedUrlSessionWizardView):
259 # DEPRECATED ##
260 # DEPRECATED ##    ...
261 # DEPRECATED ##    ...
262 # DEPRECATED ##
263 # DEPRECATED ##    @classonlymethod
264 # DEPRECATED ##    def as_view(cls, *args, **kwargs):
265 # DEPRECATED ##        kwargs.update({
266 # DEPRECATED ##            'form_list': [
267 # DEPRECATED ##                NameForm,
268 # DEPRECATED ##                QuestForm,
269 # DEPRECATED ##                ColorForm,
270 # DEPRECATED ##            ],
271 # DEPRECATED ##            'url_name': 'merlin_wizard'
272 # DEPRECATED ##        })
273 # DEPRECATED ##        return super(MerlinWizard, cls).as_view(*args, **kwargs)
274 # DEPRECATED #
275 # DEPRECATED #class UserRegisterWizardView(NamedUrlSessionWizardView):
276 # DEPRECATED ##class UserRegisterWizardView(LoginRequiredMixin, NamedUrlSessionWizardView):
277 # DEPRECATED #    # Notice that I specify a file storage instance. If you don't specify this,
278 # DEPRECATED #    # and you need to support FileField or ImageField in your forms, you'll get
279 # DEPRECATED #    # errors from Django. This is something else I think could be handled by
280 # DEPRECATED #    # the views better. Seems to me that it should just use whatever the
281 # DEPRECATED #    # default/specified storage is for the rest of your project/application.
282 # DEPRECATED #    file_storage = default_storage # FileSystemStorage()
283 # DEPRECATED #    template_name = "register_user_wizard.html"
284 # DEPRECATED #
285 # DEPRECATED #    def done(self, form_list, **kwargs):
286 # DEPRECATED #        step1_form = form_list[0]
287 # DEPRECATED #        step2_form = form_list[1]
288 # DEPRECATED #
289 # DEPRECATED #        productext = self.create_product(product_form)
290 # DEPRECATED #        shippings = self.create_shippings(productext, shipping_forms)
291 # DEPRECATED #        images = self.create_images(productext, image_forms)
292 # DEPRECATED #
293 # DEPRECATED #        if all([productext, shippings, images]):
294 # DEPRECATED #            del self.request.session["wizard_product_wizard_view"]
295 # DEPRECATED #
296 # DEPRECATED #            messages.success(self.request,
297 # DEPRECATED #                _("Your product has been created."))
298 # DEPRECATED #            return HttpResponseRedirect(self.get_success_url(productext))
299 # DEPRECATED #
300 # DEPRECATED #        messages.error(self.request, _("Something went wrong creating your "
301 # DEPRECATED #            "product. Please try again or contact support."))
302 # DEPRECATED #        return HttpResponseRedirect(reverse("register_wizard"))
303 # DEPRECATED #
304 # DEPRECATED #    #def get_form_kwargs(self, step):
305 # DEPRECATED #    #    if step == "product":
306 # DEPRECATED #    #        return {"user": self.request.user}
307 # DEPRECATED #    #    return {}
308 # DEPRECATED #
309 # DEPRECATED ## The portal should hook the slice and user creation pages
310 # DEPRECATED #
311 # DEPRECATED #def register_user(request):
312 # DEPRECATED #    
313 # DEPRECATED #    if request.method == 'POST':
314 # DEPRECATED #        form = UserRegisterForm(request.POST) # Nous reprenons les donnĂ©es
315 # DEPRECATED #        if form.is_valid():
316 # DEPRECATED #            first_name = form.cleaned_data['first_name']
317 # DEPRECATED #            last_name  = form.cleaned_data['last_name']
318 # DEPRECATED #            email      = form.cleaned_data['email']
319 # DEPRECATED #            password   = form.cleaned_data['password']
320 # DEPRECATED #            password2  = form.cleaned_data['password2']
321 # DEPRECATED #            keypair    = form.cleaned_data['keypair']
322 # DEPRECATED #            ## Ici nous pouvons traiter les donnĂ©es du formulaire
323 # DEPRECATED #            #sujet = form.cleaned_data['sujet']
324 # DEPRECATED #            #message = form.cleaned_data['message']
325 # DEPRECATED #            #envoyeur = form.cleaned_data['envoyeur']
326 # DEPRECATED #            #renvoi = form.cleaned_data['renvoi']
327 # DEPRECATED #            ## Nous pourrions ici envoyer l'e-mail grâce aux donnĂ©es que nous venons de rĂ©cupĂ©rer
328 # DEPRECATED #            #envoi = True
329 # DEPRECATED #    else:
330 # DEPRECATED #        form = UserRegisterForm()
331 # DEPRECATED #    return render(request, 'register_user.html', locals())
332 # DEPRECATED #
333 # DEPRECATED #def index(request):
334 # DEPRECATED #
335 # DEPRECATED #    WIZARD_TITLE = 'User registration'
336 # DEPRECATED #    STEP1_TITLE  = 'Enter your details'
337 # DEPRECATED #    STEP2_TITLE  = 'Select your institution'
338 # DEPRECATED #    STEP3_TITLE  = 'Authentication'
339 # DEPRECATED #    STEP4_TITLE  = 'Request a slice (optional)'
340 # DEPRECATED #    STEP5_TITLE  = 'Waiting for validation'
341 # DEPRECATED #    STEP6_TITLE  = 'Account validated'
342 # DEPRECATED #
343 # DEPRECATED #    STEP0 = render_to_string('account_validated.html', context_instance=RequestContext(request))
344 # DEPRECATED #    STEP2_HTML   = """
345 # DEPRECATED #    coucou
346 # DEPRECATED #    """
347 # DEPRECATED #    STEP4 = """
348 # DEPRECATED #    mede
349 # DEPRECATED #    """
350 # DEPRECATED #    STEP5 = render_to_string('account_validated.html', context_instance=RequestContext(request))
351 # DEPRECATED #
352 # DEPRECATED #    p = PortalPage(request)
353 # DEPRECATED #
354 # DEPRECATED #    # This is redundant with the Wizard title
355 # DEPRECATED #    p << "<h3>User registration</h3>"
356 # DEPRECATED #
357 # DEPRECATED #    sons = []
358 # DEPRECATED #    start_step = 1
359 # DEPRECATED #
360 # DEPRECATED #    # STEP 1
361 # DEPRECATED #    # If the user already exists (is logged), let's display a summary of his account details
362 # DEPRECATED #    # Otherwise propose a form to fill in
363 # DEPRECATED #    if the_user(request):
364 # DEPRECATED #        # Fill a disabled form with user info
365 # DEPRECATED #        # Please logout to register another user
366 # DEPRECATED #        sons.append(Raw(page=p, title=STEP1_TITLE, togglable=False, html=STEP0))
367 # DEPRECATED #        start_step += 1
368 # DEPRECATED #    else:
369 # DEPRECATED #        # We could pass a list of fields also, instead of retrieving them from metadata
370 # DEPRECATED #        # Otherwise we need some heuristics to display nice forms
371 # DEPRECATED #        # XXX Could we log the user in after the form is validated ?
372 # DEPRECATED #        # XXX Explain the password is for XXX
373 # DEPRECATED #        field_list = [{
374 # DEPRECATED #            'name'        : 'First name',
375 # DEPRECATED #            'field'       : 'firstname',
376 # DEPRECATED #            'type'        : 'text',
377 # DEPRECATED #            'validate_rx' : '^[a-zA-Z -]+$',
378 # DEPRECATED #            'validate_err': 'Your first name must be comprised of letters only',
379 # DEPRECATED #            'description' : 'Enter your first name',
380 # DEPRECATED #        }, {
381 # DEPRECATED #            'name'        : 'Last name',
382 # DEPRECATED #            'field'       : 'lastname',
383 # DEPRECATED #            'type'        : 'text',
384 # DEPRECATED #            'validate_rx' : '^[a-zA-Z -]+$',
385 # DEPRECATED #            'validate_err': 'Your last name must be comprised of letters only',
386 # DEPRECATED #            'description' : 'Enter your last name',
387 # DEPRECATED #        }, { 
388 # DEPRECATED #            'name'        : 'Email',
389 # DEPRECATED #            'field'       : 'email',
390 # DEPRECATED #            'type'        : 'text',
391 # DEPRECATED #            'description' : 'Enter your email address',
392 # DEPRECATED #        }, {
393 # DEPRECATED #            'name'        : 'Password',
394 # DEPRECATED #            'field'       : 'password',
395 # DEPRECATED #            'type'        : 'password',
396 # DEPRECATED #            'description' : 'Enter your password',
397 # DEPRECATED #        }, {
398 # DEPRECATED #            'name'        : 'Confirm password',
399 # DEPRECATED #            'field'       : 'password2',
400 # DEPRECATED #            'type'        : 'password',
401 # DEPRECATED #            'description' : 'Enter your password again',
402 # DEPRECATED #        }]
403 # DEPRECATED #        sons.append(CreateForm(page = p, title = STEP1_TITLE, togglable = False, object = 'local:user', fields = field_list))
404 # DEPRECATED #
405 # DEPRECATED #    # STEP 2
406 # DEPRECATED #    # If the user already exists (is logged), let's display a summary of its institution
407 # DEPRECATED #    # Otherwise propose a form to fill in (we should base our selection on the email)
408 # DEPRECATED #    if the_user(request):
409 # DEPRECATED #        # Fill a disabled form with institution
410 # DEPRECATED #        # Please logout to register another user
411 # DEPRECATED #        sons.append(Raw(page=p, title=STEP2_TITLE, togglable=False, html="User created"))
412 # DEPRECATED #        start_step += 1
413 # DEPRECATED #    else:
414 # DEPRECATED #        sons.append(CreateForm(page = p, title = STEP2_TITLE, togglable = False, object = 'slice')) #institution'))
415 # DEPRECATED #
416 # DEPRECATED #    # STEP3
417 # DEPRECATED #    # Please should your prefered authentication method
418 # DEPRECATED #    # This step should allow the user to either choose the user or managed mode in MySlice
419 # DEPRECATED #    sons.append(Raw(page = p, title = STEP3_TITLE, togglable = False, html = STEP2_HTML))
420 # DEPRECATED #
421 # DEPRECATED #    # Step 4: Request a slice (optional)
422 # DEPRECATED #    sons.append(CreateForm(page = p, title = STEP4_TITLE, togglable = False, object = 'slice'))
423 # DEPRECATED #
424 # DEPRECATED #    # Step 5: Your request is waiting for validation
425 # DEPRECATED #    # Periodic refresh
426 # DEPRECATED #    sons.append(Raw(page = p, title = STEP5_TITLE, togglable = False, html = STEP4))
427 # DEPRECATED #
428 # DEPRECATED #    # Step 6: Account validation  = welcome for newly validated users
429 # DEPRECATED #    # . delegation
430 # DEPRECATED #    # . platforms
431 # DEPRECATED #    # . slice
432 # DEPRECATED #    # . pointers
433 # DEPRECATED #    sons.append(Raw(page = p, title = STEP6_TITLE, togglable = False, html = STEP5))
434 # DEPRECATED #
435 # DEPRECATED #    wizard = Wizard(
436 # DEPRECATED #        page       = p,
437 # DEPRECATED #        title      = WIZARD_TITLE,
438 # DEPRECATED #        togglable  = False,
439 # DEPRECATED #        sons       = sons,
440 # DEPRECATED #        start_step = start_step,
441 # DEPRECATED #    )
442 # DEPRECATED #
443 # DEPRECATED #    p << wizard.render(request) # in portal page if possible
444 # DEPRECATED #
445 # DEPRECATED #    return p.render()
446
447
448 # DEPRECATED ## view for my_account
449 # DEPRECATED # class MyAccountView(TemplateView):
450 # DEPRECATED #    template_name = "my_account.html"
451 # DEPRECATED #    
452 # DEPRECATED #    def from_process(self, request, **cleaned_data): 
453 # DEPRECATED #        #if request.method == 'POST':
454 # DEPRECATED #         #       if request.POST['submit_name']:
455 # DEPRECATED #        if 'fname' in request.POST:            
456 # DEPRECATED #                messsag= "Got Name"
457 # DEPRECATED #                #return render(request, 'portal/my_account.html')
458 # DEPRECATED #                #response = HttpResponse("Here's the text of the Web page.")    
459 # DEPRECATED #                return HttpResponse(message)
460 # DEPRECATED #            
461 # DEPRECATED #    def get_context_data(self, **kwargs):
462 # DEPRECATED #        page = Page(self.request)
463 # DEPRECATED #        context = super(MyAccountView, self).get_context_data(**kwargs)
464 # DEPRECATED #        context['person']   = self.request.user
465 # DEPRECATED #        # XXX This is repeated in all pages
466 # DEPRECATED #        # more general variables expected in the template
467 # DEPRECATED #        context['title'] = 'User Profile Page'
468 # DEPRECATED #        # the menu items on the top
469 # DEPRECATED #        context['topmenu_items'] = topmenu_items('my_account', self.request)
470 # DEPRECATED #        # so we can sho who is logged
471 # DEPRECATED #        context['username'] = the_user(self.request)
472 # DEPRECATED #        context.update(page.prelude_env())
473 # DEPRECATED #        return context
474
475
476
477 # View for my_account form
478 def my_account(request):
479     return render(request, 'my_account.html', {
480         #'form': form,
481         'topmenu_items': topmenu_items('My Account', request),
482         'username': the_user (request)
483     })
484
485
486 #my_acc form value processing
487 def acc_process(request):
488     # getting the user_id from the session [now hardcoded]
489     get_user = PendingUser.objects.get(id='1') # here we will get the id/email from session e.g., person.email
490     if 'submit_name' in request.POST:
491         edited_first_name =  request.POST['fname']
492         edited_last_name =  request.POST['lname']
493         #email = 'test_email@gmail.com'
494         #password = 'test_pp'
495         #message = 'F_Name: %s L_name: %s dummy_pp: %s' % (first_name, last_name, password)
496         #site = None
497         
498         # insert into DB [needed for registration page]
499         #approach borrowed from register view     
500         #new_user = PendingUser.objects.create_inactive_user(edited_first_name, edited_last_name, email,  password, site) 
501         #conventional approach
502         #b = PendingUser(first_name=edited_first_name, last_name=edited_last_name)
503         #b.save()
504         
505         # select and update [will be used throughout this view]
506         # select the logged in user [for the moment hard coded]
507         #get_user = PendingUser.objects.get(id='1') # here we will get the id/email from session e.g., person.email
508         # update first and last name
509         get_user.first_name = edited_first_name
510         get_user.last_name = edited_last_name
511         get_user.save() 
512
513         return HttpResponse('Success: Name Updated!!')       
514     elif 'submit_pass' in request.POST:
515         edited_password = request.POST['password']
516         # select the logged in user [for the moment hard coded]
517         #get_user = PendingUser.objects.get(id='1') # here we will get the id/email from session e.g., person.email
518         # update password
519         get_user.password = edited_password
520         get_user.save()
521         return HttpResponse('Success: Password Changed!!')
522     elif 'generate' in request.POST:
523         #import os
524         #from M2Crypto import Rand, RSA, BIO
525
526         KEY_LENGTH = 2048
527
528         def blank_callback():
529             "Replace the default dashes"
530             return
531
532         # Random seed
533         Rand.rand_seed (os.urandom (KEY_LENGTH))
534         # Generate key pair
535         key = RSA.gen_key (KEY_LENGTH, 65537, blank_callback)
536         # Create memory buffers
537         pri_mem = BIO.MemoryBuffer()
538         pub_mem = BIO.MemoryBuffer()
539         # Save keys to buffers
540         key.save_key_bio(pri_mem, None)
541         key.save_pub_key_bio(pub_mem)
542
543         # Get keys 
544         public_key = pub_mem.getvalue()
545         private_key = pri_mem.getvalue()
546         # Saving to DB
547         keypair = '{"user_public_key":"'+ public_key + '", "user_private_key":"'+ private_key + '"}'
548         keypair = re.sub("\r", "", keypair)
549         keypair = re.sub("\n", "\\n", keypair)
550         #keypair = keypair.rstrip('\r\n')
551         keypair = ''.join(keypair.split())
552         get_user.keypair = keypair
553         get_user.save()
554         return HttpResponse('Success: New Keypair Generated! %s' % keypair)
555
556     elif 'upload_key' in request.POST:
557         up_file = request.FILES['pubkey']
558         file_content =  up_file.read()
559         file_name = up_file.name
560         file_extension = os.path.splitext(file_name)[1] 
561         allowed_extension =  ['.pub','.txt']
562         if file_extension in allowed_extension and re.search(r'ssh-rsa',file_content):
563             file_content = '{"user_public_key":"'+ file_content +'"}'
564             file_content = re.sub("\r", "", file_content)
565             file_content = re.sub("\n", "\\n",file_content)
566             file_content = ''.join(file_content.split())
567             get_user.keypair = file_content
568             get_user.save()
569             return HttpResponse('Success: Publickey uploaded! Old records overwritten')
570         else:
571             return HttpResponse('Please upload a valid RSA public key [.txt or .pub].')    
572         
573     else:
574         message = 'You submitted an empty form.'
575         return HttpResponse(message)
576
577 def register_4m_f4f(request):
578     #return render(request, 'register_4m_f4f.html')
579
580 #def reg_4m_f4f_process(request):
581     if 'submit' in request.POST:
582         #get_email = PendingUser.objects.get(email)
583         reg_fname = request.POST['firstname']
584         reg_lname = request.POST['lastname']
585         reg_aff = request.POST['affiliation']
586         reg_email = request.POST['email'].lower()
587         
588         #POST value validation  
589         if (re.search(r'^[\w+\s.@+-]+$', reg_fname)==None):
590             messages.error(request, 'First Name may contain only letters, numbers, spaces and @/./+/-/_ characters.')
591             #return HttpResponse("Only Letters, Numbers, - and _ allowd in First Name")
592             return render(request, 'register_4m_f4f.html')
593         if (re.search(r'^[\w+\s.@+-]+$', reg_lname) == None):
594             messages.error(request, 'Last Name may contain only letters, numbers, spaces and @/./+/-/_ characters.')
595             #return HttpResponse("Only Letters, Numbers, - and _ is allowed in Last name")
596             return render(request, 'register_4m_f4f.html')
597         if (re.search(r'^[\w+\s.@+-]+$', reg_aff) == None):
598             messages.error(request, 'Affiliation may contain only letters, numbers, spaces and @/./+/-/_ characters.')
599             #return HttpResponse("Only Letters, Numbers and _ is allowed in Affiliation")
600             return render(request, 'register_4m_f4f.html')
601         if PendingUser.objects.filter(email__iexact=reg_email):
602             messages.error(request, 'Email already registered.Please provide a new email address.')
603             #return HttpResponse("Email Already exists")
604             return render(request, 'register_4m_f4f.html')
605         if 'generate' in request.POST['question']:
606             #import os
607             #from M2Crypto import Rand, RSA, BIO
608             
609             KEY_LENGTH = 2048
610
611             def blank_callback():
612                 "Replace the default dashes"
613                 return
614
615             # Random seed
616             Rand.rand_seed (os.urandom (KEY_LENGTH))
617             # Generate key pair
618             key = RSA.gen_key (KEY_LENGTH, 65537, blank_callback)
619             # Create memory buffers
620             pri_mem = BIO.MemoryBuffer()
621             pub_mem = BIO.MemoryBuffer()
622             # Save keys to buffers
623             key.save_key_bio(pri_mem, None)
624             key.save_pub_key_bio(pub_mem)
625             # Get keys 
626             public_key = pub_mem.getvalue()
627             private_key = pri_mem.getvalue()
628             # Saving to DB
629             keypair = '{"user_public_key":"'+ public_key + '", "user_private_key":"'+ private_key + '"}'
630             keypair = re.sub("\r", "", keypair)
631             keypair = re.sub("\n", "\\n", keypair)
632             #keypair = keypair.rstrip('\r\n')
633             keypair = ''.join(keypair.split())
634             #return HttpResponse(keypair)
635         else:
636             up_file = request.FILES['user_public_key']
637             file_content =  up_file.read()
638             file_name = up_file.name
639             file_extension = os.path.splitext(file_name)[1]
640             allowed_extension =  ['.pub','.txt']
641             if file_extension in allowed_extension and re.search(r'ssh-rsa',file_content):
642                 keypair = '{"user_public_key":"'+ file_content +'"}'
643                 keypair = re.sub("\r", "", keypair)
644                 keypair = re.sub("\n", "\\n",keypair)
645                 keypair = ''.join(keypair.split())
646             else:
647                 return HttpResponse('Please upload a valid RSA public key [.txt or .pub].')
648
649         b = PendingUser(first_name=reg_fname, last_name=reg_lname, affiliation=reg_aff, 
650                         email=reg_email, password=request.POST['password'], keypair=keypair)
651         b.save()
652
653         return render(request, 'user_register_complete.html')
654     return render(request, 'register_4m_f4f.html')        
655     
656
657 # view for contact form
658 def contact(request):
659     if request.method == 'POST': # If the form has been submitted...
660         form = ContactForm(request.POST) # A form bound to the POST data
661         if form.is_valid(): # All validation rules pass
662             # Process the data in form.cleaned_data
663             first_name = form.cleaned_data['first_name']
664             last_name = form.cleaned_data['last_name']
665             affiliation = form.cleaned_data['affiliation']
666             subject = form.cleaned_data['subject']
667             message = form.cleaned_data['message']
668             email = form.cleaned_data['email'] # email of the sender
669             cc_myself = form.cleaned_data['cc_myself']
670
671             recipients = ['yasin.upmc@gmail.com']
672             if cc_myself:
673                 recipients.append(email)
674
675             from django.core.mail import send_mail
676             send_mail("Onelab user submitted a query ", [first_name,last_name,affiliation,subject,message], email, recipients)
677             return render(request,'contact_sent.html') # Redirect after POST
678     else:
679         form = ContactForm() # An unbound form
680     
681     return render(request, 'contact.html', {
682         'form': form,
683         'topmenu_items': topmenu_items('Contact Us', request),
684         'username': the_user (request)
685
686     })
687
688
689 def slice_request(request):
690     if request.method == 'POST': # If the form has been submitted...
691         form = SliceRequestForm(request.POST) # A form bound to the POST data
692         if form.is_valid(): # All validation rules pass
693             # Process the data in form.cleaned_data
694             slice_name = form.cleaned_data['slice_name']
695             number_of_nodes = form.cleaned_data['number_of_nodes']
696             type_of_nodes = form.cleaned_data['type_of_nodes']
697             purpose = form.cleaned_data['purpose']
698             email = form.cleaned_data['email'] # email of the sender
699             cc_myself = form.cleaned_data['cc_myself']
700
701             recipients = ['yasin.upmc@gmail.com','jordan.auge@lip6.fr']
702             if cc_myself:
703                 recipients.append(email)
704
705             from django.core.mail import send_mail
706             send_mail("Onelab New Slice request form submitted", [slice_name,number_of_nodes,type_of_nodes,purpose], email, recipients)
707             return render(request,'slicereq_recvd.html') # Redirect after POST
708     else:
709         form = SliceRequestForm() # An unbound form
710
711 #    template_env = {}
712 #    template_env['form'] = form
713 #    template_env['topmenu_items'] = topmenu_items('Request a slice', request) 
714 #    template_env['unfold1_main'] = render(request, 'slice_request_.html', {
715 #        'form': form,
716 #    })
717 #    from django.shortcuts                import render_to_response
718 #    from django.template                 import RequestContext
719 #    return render_to_response ('view-unfold1.html',template_env,
720 #                               context_instance=RequestContext(request))
721
722     return render(request, 'slice_request.html', {
723         'form': form,
724         'topmenu_items': topmenu_items('Request a slice', request),
725         'username': the_user (request) 
726     })
727
728
729 class PresViewView(TemplateView):
730     template_name = "view-unfold1.html"
731
732     def get_context_data(self, **kwargs):
733
734         page = Page(self.request)
735
736         pres_view = PresView(page = page)
737
738         context = super(PresViewView, self).get_context_data(**kwargs)
739
740         #context['ALL_STATIC'] = "all_static"
741         context['unfold1_main'] = pres_view.render(self.request)
742
743         # XXX This is repeated in all pages
744         # more general variables expected in the template
745         context['title'] = 'Test view that combines various plugins'
746         # the menu items on the top
747         context['topmenu_items'] = topmenu_items('PresView', self.request)
748         # so we can sho who is logged
749         context['username'] = the_user(self.request)
750
751         prelude_env = page.prelude_env()
752         context.update(prelude_env)
753
754         return context
755
756 def json_me(config_file,type):
757     json_answer = ''
758     for ligne in config_file:
759         if not ligne.startswith('#'):
760             args = ligne.split(';')
761             json_answer += str('{ "name": "' + args[0] + '" ,"id":"' + args[1]  + '" ,"descriptif":"' + args[2]+'"')
762             if type!="dynamic":
763                 json_answer += str(',"contraints":')
764                 if args[3]=="":
765                     json_answer += str('""')
766                 else:
767                     json_answer += str(args[3])
768             json_answer += str('},')
769     return json_answer[:-1]
770
771
772 DIR = '/var/myslice/'
773 STATIC = '%s/config_method_static' % DIR
774 DYNAMIC = '%s/config_method_dynamic' % DIR
775 ANIMATION = '%s/config_method_animation' % DIR
776
777 def pres_view_methods(request, type):
778
779     if type ==None:
780         return 0
781     elif type =="static":
782         config = open(STATIC, "r")
783         json_answer = str('{ "options": [')
784         json_answer += str(json_me(config,"static"))
785         json_answer += str('] }')
786         config.close()
787     elif type =="dynamic":
788         config = open(DYNAMIC, "r")
789         json_answer = str('{ "options": [')
790         json_answer += str(json_me(config,"dynamic"))
791         json_answer += str('] }')
792         config.close()
793     elif type =="animation":
794         config = open(ANIMATION, "r")
795         json_answer = str('{ "options": [')
796         json_answer += str(json_me(config,"animation"))
797         json_answer += str('] }')
798         config.close()
799     elif type =="all":
800         config = open(STATIC, "r")
801         json_answer = str('{ "static": [')
802         json_answer += str(json_me(config,"static"))
803         json_answer += str('],')
804         json_answer += str('"dynamic": [')
805         config.close()
806         config = open(DYNAMIC, "r")
807         json_answer += str(json_me(config,"dynamic"))
808         json_answer += str('],')
809         json_answer += str('"animation": [')
810         config.close()
811         config = open(ANIMATION, "r")
812         json_answer += str(json_me(config,"animation"))
813         json_answer += str('] }')
814         config.close()
815     else:
816         return 0
817     return HttpResponse (json_answer, mimetype="application/json")
818
819 def pres_view_animation(request, constraints, id):
820
821 # sites crees depuis 2008
822 # static.py?contraints=']date_created':1262325600&id='name_id"'
823
824     # method = request.getvalue('method') #ex : GetSites
825     #constraints = "']date_created':1262325600"
826     #id = "2"
827
828     if id == None:
829         return 0
830
831     # method = 'GetSites'#request.getvalue('method') #ex : GetSites
832     # constraints = {}#request.getvalue('constraints') // nul = {}
833     # response_field = "'site_id','name','date_created'"#request.getvalue('response_field')
834
835     config_file = open(ANIMATION, "r")
836     for ligne in config_file:
837         if not ligne.startswith('#'):
838             ligne = ligne.split('\n')
839             first = ligne[0].split(';')
840             if (str(first[1]) == str(id)):
841                 save = first
842     config_file.close()
843
844     #Les print_method, print_option sont definis par le client (js)
845     #Les animations acceptent que les connexions anonymous
846     # args = "postmsg;animation;;;anonymous;https://www.planet-lab.eu/PLCAPI/;"
847     args = ";;"+str(save[8])+";"+str(save[9])+";anonymous;"+str(save[5])+";"+str(save[6])+";{"+str(constraints)+"};"+str(save[7])+";"
848
849
850     #Creation d'un objet event
851     event = Event(args)
852     cmd = [{"params": {
853             "data": {
854                 "print_options": event.print_options,
855                 "print_method": event.print_method,
856                 "message": event.data
857             }
858         }
859     }]
860
861     json_answer = json.dumps(cmd)
862     return HttpResponse (json_answer, mimetype="application/json")
863
864 def pres_view_static(request, constraints, id):
865     #constraints = "']date_created':1262325600"
866     #id = "2"
867
868     # method = 'GetSites'#request.getvalue('method') #ex : GetSites
869     # constraints = {}#request.getvalue('constraints') // nul = {}
870     # response_field = "'site_id','name','date_created'"#request.getvalue('response_field')
871
872     config_file = open(STATIC, "r")
873     for ligne in config_file:
874         if not ligne.startswith('#'):
875             ligne = ligne.split('\n')
876             first = ligne[0].split(';')
877             if (str(first[1]) == str(id)):
878                 save = first
879     config_file.close()
880
881     #Les print_method, print_option sont definis par le client (js)
882     #Les animations acceptent que les connexions anonymous
883     # args = "postmsg;animation;;;anonymous;https://www.planet-lab.eu/PLCAPI/;"
884     args = ";;"+str(save[8])+";"+str(save[9])+";anonymous;"+str(save[5])+";"+str(save[6])+";{"+str(constraints)+"};"+str(save[7])+";"
885
886
887     #Creation d'un objet event
888     event = Event(args)
889     cmd = [{"params": {
890             "data": {
891                 "print_options": event.print_options,
892                 "print_method": event.print_method,
893                 "message": event.data
894             }
895         }
896     }]
897
898     json_answer = json.dumps(cmd)
899     return HttpResponse (json_answer, mimetype="application/json")
900
901 class ValidatePendingView(TemplateView):
902     template_name = "validate_pending.html"
903
904     def get_context_data(self, **kwargs):
905         # We might have slices on different registries with different user accounts 
906         # 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
907         # Different registries mean different identities, unless we identify via SFA HRN or have associated the user email to a single hrn
908
909         #messages.info(self.request, 'You have logged in')
910         page = Page(self.request)
911
912         ctx_my_authorities = {}
913         ctx_delegation_authorities = {}
914
915
916         # The user need to be logged in
917         if the_user(self.request):
918             # Who can a PI validate:
919             # His own authorities + those he has credentials for.
920             # In MySlice we need to look at credentials also.
921             
922
923             # XXX This will have to be asynchroneous. Need to implement barriers,
924             # for now it will be sufficient to have it working statically
925
926             # get user_id to later on query accounts
927             # XXX Having real query plan on local tables would simplify all this
928             # XXX $user_email is still not available for local tables
929             #user_query = Query().get('local:user').filter_by('email', '==', '$user_email').select('user_id')
930             user_query = Query().get('local:user').filter_by('email', '==', the_user(self.request)).select('user_id')
931             user, = execute_query(self.request, user_query)
932             user_id = user['user_id']
933
934             # Query manifold to learn about available SFA platforms for more information
935             # In general we will at least have the portal
936             # For now we are considering all registries
937             all_authorities = []
938             platform_ids = []
939             sfa_platforms_query = Query().get('local:platform').filter_by('gateway_type', '==', 'sfa').select('platform_id', 'platform', 'auth_type')
940             sfa_platforms = execute_query(self.request, sfa_platforms_query)
941             for sfa_platform in sfa_platforms:
942                 print "SFA PLATFORM > ", sfa_platform['platform']
943                 if not 'auth_type' in sfa_platform:
944                     continue
945                 auth = sfa_platform['auth_type']
946                 if not auth in all_authorities:
947                     all_authorities.append(auth)
948                 platform_ids.append(sfa_platform['platform_id'])
949
950             # We can check on which the user has authoritity credentials = PI rights
951             credential_authorities = set()
952             credential_authorities_expired = set()
953
954             # User account on these registries
955             user_accounts_query = Query.get('local:account').filter_by('user_id', '==', user_id).filter_by('platform_id', 'included', platform_ids).select('config')
956             user_accounts = execute_query(self.request, user_accounts_query)
957             #print "=" * 80
958             #print user_accounts
959             #print "=" * 80
960             for user_account in user_accounts:
961                 config = json.loads(user_account['config'])
962                 creds = []
963                 if 'authority_credentials' in config:
964                     for authority_hrn, credential in config['authority_credentials'].items():
965                         #if credential is not expired:
966                         credential_authorities.add(authority_hrn)
967                         #else
968                         #    credential_authorities_expired.add(authority_hrn)
969                 if 'delegated_authority_credentials' in config:
970                     for authority_hrn, credential in config['delegated_authority_credentials'].items():
971                         #if credential is not expired:
972                         credential_authorities.add(authority_hrn)
973                         #else
974                         #    credential_authorities_expired.add(authority_hrn)
975
976             print 'credential_authorities =', credential_authorities
977             print 'credential_authorities_expired =', credential_authorities_expired
978
979             # ** Where am I a PI **
980             # For this we need to ask SFA (of all authorities) = PI function
981             pi_authorities_query = Query.get('user').filter_by('user_hrn', '==', '$user_hrn').select('pi_authorities')
982             pi_authorities_tmp = execute_query(self.request, pi_authorities_query)
983             pi_authorities = set()
984             for pa in pi_authorities_tmp:
985                 pi_authorities |= set(pa['pi_authorities'])
986
987             print "pi_authorities =", pi_authorities
988             
989             # My authorities + I have a credential
990             pi_credential_authorities = pi_authorities & credential_authorities
991             pi_no_credential_authorities = pi_authorities - credential_authorities - credential_authorities_expired
992             pi_expired_credential_authorities = pi_authorities & credential_authorities_expired
993             # Authorities I've been delegated PI rights
994             pi_delegation_credential_authorities = credential_authorities - pi_authorities
995             pi_delegation_expired_authorities = credential_authorities_expired - pi_authorities
996
997             print "pi_credential_authorities =", pi_credential_authorities
998             print "pi_no_credential_authorities =", pi_no_credential_authorities
999             print "pi_expired_credential_authorities =", pi_expired_credential_authorities
1000             print "pi_delegation_credential_authorities = ", pi_delegation_credential_authorities
1001             print "pi_delegation_expired_authorities = ", pi_delegation_expired_authorities
1002
1003             # Summary intermediary
1004             pi_my_authorities = pi_credential_authorities | pi_no_credential_authorities | pi_expired_credential_authorities
1005             pi_delegation_authorities = pi_delegation_credential_authorities | pi_delegation_expired_authorities
1006
1007             print "--"
1008             print "pi_my_authorities = ", pi_my_authorities
1009             print "pi_delegation_authorities = ", pi_delegation_authorities
1010
1011             # Summary all
1012             queried_pending_authorities = pi_my_authorities | pi_delegation_authorities
1013             print "----"
1014             print "queried_pending_authorities = ", queried_pending_authorities
1015             
1016             # Pending requests + authorities
1017             #pending_users = PendingUser.objects.filter(authority_hrn__in = queried_pending_authorities).all() 
1018             #pending_slices = PendingSlice.objects.filter(authority_hrn__in = queried_pending_authorities).all() 
1019             pending_users = PendingUser.objects.all()
1020             pending_slices = PendingSlice.objects.all()
1021
1022             # Dispatch requests and build the proper structure for the template:
1023
1024             print "pending users =", pending_users
1025             print "pending slices =", pending_slices
1026
1027             for user in pending_users:
1028                 auth_hrn = user.authority_hrn
1029                 if not auth_hrn:
1030                     auth_hrn = "ple.upmc" # XXX HARDCODED
1031
1032                 request = {}
1033                 request['type'] = 'user'
1034                 request['id'] = 'TODO' # XXX in DB ?
1035                 request['timestamp'] = 'TODO' # XXX in DB ?
1036                 request['details'] = "%s %s <%s>" % (user.first_name, user.last_name, user.email)
1037
1038                 if auth_hrn in pi_my_authorities:
1039                     dest = ctx_my_authorities
1040
1041                     # define the css class
1042                     if auth_hrn in pi_credential_authorities:
1043                         request['allowed'] = 'allowed'
1044                     elif auth_hrn in pi_expired_credential_authorities:
1045                         request['allowed'] = 'expired'
1046                     else: # pi_no_credential_authorities
1047                         request['allowed'] = 'denied'
1048
1049                 elif auth_hrn in pi_delegation_authorities:
1050                     dest = ctx_delegation_authorities
1051
1052                     if auth_hrn in pi_delegation_credential_authorities:
1053                         request['allowed'] = 'allowed'
1054                     else: # pi_delegation_expired_authorities
1055                         request['allowed'] = 'expired'
1056
1057                 else:
1058                     continue
1059                     
1060                 if not auth_hrn in dest:
1061                     dest[auth_hrn] = []
1062                 print "auth_hrn [%s] was added %r" % (auth_hrn, request)
1063                 dest[auth_hrn].append(request) 
1064
1065             for slice in pending_slices:
1066                 auth_hrn = slice.authority_hrn
1067                 if not auth_hrn:
1068                     auth_hrn = "ple.upmc" # XXX HARDCODED
1069
1070                 request = {}
1071                 request['type'] = 'slice'
1072                 request['id'] = 'TODO' # XXX in DB ?
1073                 request['timestamp'] = 'TODO' # XXX in DB ?
1074                 request['details'] = "Number of nodes: %d -- Type of nodes: %s<br/>%s" % ('TODO', 'TODO', 'TODO') # XXX 
1075                 if auth_hrn in pi_my_authorities:
1076                     dest = ctx_my_authorities
1077
1078                     # define the css class
1079                     if auth_hrn in pi_credential_authorities:
1080                         request['allowed'] = 'allowed'
1081                     elif auth_hrn in pi_expired_credential_authorities:
1082                         request['allowed'] = 'expired'
1083                     else: # pi_no_credential_authorities
1084                         request['allowed'] = 'denied'
1085
1086                 elif auth_hrn in pi_delegation_authorities:
1087                     dest = ctx_delegation_authorities
1088
1089                     if auth_hrn in pi_delegation_credential_authorities:
1090                         request['allowed'] = 'allowed'
1091                     else: # pi_delegation_expired_authorities
1092                         request['allowed'] = 'expired'
1093                     
1094                 if not auth_hrn in dest:
1095                     dest[auth_hrn] = []
1096                 dest[auth_hrn].append(request) 
1097
1098         context = super(ValidatePendingView, self).get_context_data(**kwargs)
1099         context['my_authorities']   = ctx_my_authorities
1100         context['delegation_authorities'] = ctx_delegation_authorities
1101
1102         # XXX This is repeated in all pages
1103         # more general variables expected in the template
1104         context['title'] = 'Test view that combines various plugins'
1105         # the menu items on the top
1106         context['topmenu_items'] = topmenu_items('Dashboard', self.request) 
1107         # so we can sho who is logged
1108         context['username'] = the_user(self.request) 
1109
1110         # XXX We need to prepare the page for queries
1111         #context.update(page.prelude_env())
1112
1113         return context