Please note that the CVS and issue trackers have moved to GitHub. These Trac pages are no longer kept up-to-date.

root/seattle/trunk/seattlegeni/website/html/views.py@6045

Revision 6045, 36.4 KB (checked in by gpress, 7 years ago)

addresses ticket #1094, the android installer should now properly credit donations.

Line 
1"""
2<Program>
3  views.py
4
5<Started>
6  October, 2008
7
8<Author>
9  Ivan Beschastnikh
10  Jason Chen
11  Justin Samuel
12
13<Purpose>
14  This module defines the functions that correspond to each possible request
15  made through the html frontend. The urls.py file in this same directory
16  lists the url paths which can be requested and the corresponding function name
17  in this file which will be invoked.
18"""
19
20import os
21import sys
22import shutil
23import subprocess
24import xmlrpclib
25
26# Needed to escape characters for the Android referrer...
27import urllib
28
29from django.http import HttpResponse
30from django.http import HttpResponseRedirect
31from django.contrib.auth.forms import AuthenticationForm
32from django.contrib.auth.decorators import login_required
33from django.core.urlresolvers import reverse
34
35#Used to display meaningful OpenID/OAuth error messages to the user
36from django.contrib.messages.api import get_messages
37from django.shortcuts import render_to_response, redirect
38from social_auth.utils import setting
39from django.template import RequestContext
40# Any requests that change state on the server side (e.g. result in database
41# modifications) need to be POST requests. This is for protecting against
42# CSRF attacks (part, but not all, of that is our use of the CSRF middleware
43# which only checks POST requests). Sometimes we use this decorator, sometimes
44# we check the request type inside the view function.
45from django.views.decorators.http import require_POST
46
47from django.views.generic.simple import direct_to_template
48from django.views.generic.simple import redirect_to
49
50# Make available all of our own standard exceptions.
51from seattlegeni.common.exceptions import *
52
53# This is the logging decorator use use.
54from seattlegeni.common.util.decorators import log_function_call
55from seattlegeni.common.util.decorators import log_function_call_without_return
56
57# For user registration input validation
58from seattlegeni.common.util import validations
59
60from seattlegeni.common.util import log
61
62from seattlegeni.website import settings
63
64# All of the work that needs to be done is passed through the controller interface.
65from seattlegeni.website.control import interface
66
67from seattlegeni.website.html import forms
68
69from seattle import repyhelper
70from seattle import repyportability
71
72repyhelper.translate_and_import('rsa.repy')
73
74
75
76
77
78class LoggedInButFailedGetGeniUserError(Exception):
79  """
80  <Purpose>
81  Indicates that a function tried to get a GeniUser record, and failed;
82  while having passed the @login_required decorator. This means that a
83  DjangoUser is logged in, but there is no corresponding GeniUser record.
84 
85  This exception should only be thrown from _validate_and_get_geniuser,
86  and caught by methods with @login_required decorators.
87  """
88
89
90
91
92
93def _state_key_file_to_publickey_string(key_file_name):
94  """
95  Read a public key file from the the state keys directory and return it in
96  a key string format.
97  """
98  fullpath = os.path.join(settings.SEATTLECLEARINGHOUSE_STATE_KEYS_DIR, key_file_name)
99  return rsa_publickey_to_string(rsa_file_to_publickey(fullpath))
100
101
102
103
104
105# The key used as the state key for new donations.
106ACCEPTDONATIONS_STATE_PUBKEY = _state_key_file_to_publickey_string("acceptdonation.publickey")
107
108
109
110
111
112def error(request):
113  """
114  <Purpose>
115    If a OpenID/OAuth backend itself has an error(not a user or Seattle Clearinghouse's fault)
116    a user will get redirected here.  This can happen if the backend rejects the user or from
117    user cancelation.
118
119  <Arguments>
120    request:
121      An HTTP request object.
122
123  <Exceptions>
124    None
125
126  <Side Effects>
127    None
128
129  <Returns>
130    An HTTP response object that represents the error page.
131  """
132  #Retrieve information which caused an error
133  messages = get_messages(request)
134  info =''
135  try:
136    user = _validate_and_get_geniuser(request)
137    return profile(request, info, info, messages)
138  except:
139    return _show_login(request, 'accounts/login.html', {'messages' : messages}) 
140
141
142
143
144
145@login_required
146def associate_error(request):
147  """
148  <Purpose>
149    If an error occured during the OpenId/OAuth association process a user will get
150    redirected here.
151  <Arguments>
152    request:
153      An HTTP request object.
154    backend:
155      An OpenID/OAuth backend ex google,facebook etc.
156  <Exceptions>
157    None
158  <Side Effects>
159    None
160  <Returns>
161    An HTTP response object that represents the associate_error page.
162  """
163  info=''
164  error_msg = "Whoops, this account is already linked to another Seattle Clearninghouse user."
165  return profile(request, info, error_msg)
166
167
168
169
170
171def auto_register(request,backend=None,error_msgs=''):
172  """
173  <Purpose>
174  Part of the SOCIAL_AUTH_PIPELINE whose order is mapped in settings.py.  If
175  a user logs in with a OpenID/OAuth account and that account is not yet linked
176  with a Clearinghouse account, he gets redirected here.
177  If a valid username is entered then a new Seattle Clearinghouse user is created.
178
179  <Arguments>
180    request:
181      An HTTP request object.
182    backend:
183      An OpenID/OAuth backend ex google,facebook etc.
184  <Exceptions>
185
186  <Side Effects>
187    A new Seattle Clearinghouse user is created.
188  <Returns>
189    If a user passes in a valid username he continues the pipeline and moves
190    forward in the auto register process.
191  """
192  # Check if a username is provided
193  username_form = forms.AutoRegisterForm()
194  if request.method == 'POST' and request.POST.get('username'):
195    name = setting('SOCIAL_AUTH_PARTIAL_PIPELINE_KEY', 'partial_pipeline')
196    username_form = forms.AutoRegisterForm(request.POST)
197    if username_form.is_valid():
198      username = username_form.cleaned_data['username']
199      try:
200        interface.get_user_without_password(username)
201        error_msgs ='That username is already in use.'
202      except DoesNotExistError:
203        request.session['saved_username'] = request.POST['username']
204        backend = request.session[name]['backend']
205        return redirect('socialauth_complete', backend=backend) 
206  name = setting('SOCIAL_AUTH_PARTIAL_PIPELINE_KEY', 'partial_pipeline')
207  backend=request.session[name]['backend']
208  return render_to_response('accounts/auto_register.html', {'backend' : backend, 'error_msgs' : error_msgs, 'username_form' : username_form}, RequestContext(request))
209
210
211
212
213
214@log_function_call_without_return
215@login_required
216def profile(request, info="", error_msg="", messages=""):
217  """
218  <Purpose>
219    Display information about the user account.
220    This method requires the request to represent a valid logged
221    in user. See the top-level comment about the @login_required()
222    decorator to achieve this property.  User account is editable through this
223    method.
224  <Arguments>
225    request:
226      An HTTP request object. 
227    info:
228      Additional message to display at the top of the page in a green box.
229    error_msg:
230      Additional message to display at top of the page in a red box.
231    messages
232      Django social auth error message
233  <Exceptions>
234    None
235  <Side Effects>
236    None
237  <Returns>
238    An HTTP response object that represents the profile page.
239  """
240  try:
241    user = _validate_and_get_geniuser(request)
242  except LoggedInButFailedGetGeniUserError:
243    return _show_failed_get_geniuser_page(request)
244 
245  email_form = forms.gen_edit_user_form(instance=user)
246  affiliation_form = forms.gen_edit_user_form(instance=user)
247  password_form = forms.EditUserPasswordForm()
248   
249  if request.method == 'POST':
250    if 'affiliation' in request.POST:
251       affiliation_form = forms.gen_edit_user_form(('affiliation',), request.POST, instance=user)
252       if affiliation_form.is_valid():
253         new_affiliation = affiliation_form.cleaned_data['affiliation']
254         interface.change_user_affiliation(user, new_affiliation)
255         info ="Affiliation has been successfully changed to %s." % (user.affiliation)
256    elif 'email' in request.POST:
257       email_form = forms.gen_edit_user_form(('email',), request.POST, instance=user)
258       if email_form.is_valid():
259         new_email = email_form.cleaned_data['email']
260         interface.change_user_email(user, new_email)
261         info ="Email has been successfully changed to %s." % (user.email)
262    elif 'password1' in request.POST:
263       password_form = forms.EditUserPasswordForm( request.POST, instance=user)
264       if password_form.is_valid():
265         new_password = password_form.cleaned_data['password1']
266         interface.change_user_password(user, new_password)
267         info ="Password has been successfully changed"
268 
269  username = user.username
270  affiliation = user.affiliation
271  email = user.email
272  port = user.usable_vessel_port
273  has_privkey = user.user_privkey != None
274  #currently not used, needed if editing user port is allowed
275  #port_range = interface.get_useable_ports()
276  #port_range_min = port_range[0]
277  #port_range_max = port_range[-1]
278 
279  return direct_to_template(request, 'control/profile.html',
280                            {'email_form' : email_form,
281                             'affiliation_form' : affiliation_form,
282                             'password_form' : password_form,
283                             'username' : username,
284                             'affiliation' : affiliation,
285                             'email' : email,
286                             'port' : port,
287                             'api_key' : user.api_key,
288                             'has_privkey' : has_privkey,
289                             #'port_range_min' : port_range_min,
290                             #'port_range_max' : port_range_max,
291                             'info' : info,
292                             'error_msg' : error_msg,
293                             'messages' : messages})
294
295
296
297
298
299def register(request):
300  try:
301    # check to see if a user is already logged in. if so, redirect them to profile.
302    user = interface.get_logged_in_user(request)
303  except DoesNotExistError:
304    pass
305  else:
306    return HttpResponseRedirect(reverse("profile"))
307 
308  page_top_errors = []
309  if request.method == 'POST':
310   
311    #TODO: what if the form data isn't in the POST request? we need to check for this.
312    form = forms.GeniUserCreationForm(request.POST, request.FILES)
313    # Calling the form's is_valid() function causes all form "clean_..." methods to be checked.
314    # If this succeeds, then the form input data is validated per field-specific cleaning checks. (see forms.py)
315    # However, we still need to do some checks which aren't doable from inside the form class.
316    if form.is_valid():
317      username = form.cleaned_data['username']
318      password = form.cleaned_data['password1']
319      affiliation = form.cleaned_data['affiliation']
320      email = form.cleaned_data['email']
321      pubkey = form.cleaned_data['pubkey']
322     
323      try:
324        validations.validate_username_and_password_different(username, password)
325      except ValidationError, err:
326        page_top_errors.append(str(err))
327     
328      # NOTE: gen_upload_choice turns out to be a *string* when retrieved, hence '2'
329      if form.cleaned_data['gen_upload_choice'] == '2' and pubkey == None:
330        page_top_errors.append("Please select a public key to upload.")
331     
332      # only proceed with registration if there are no validation errors
333      if page_top_errors == []:
334        try:
335          # we should never error here, since we've already finished validation at this point.
336          # but, just to be safe...
337          user = interface.register_user(username, password, email, affiliation, pubkey)
338        except ValidationError, err:
339          page_top_errors.append(str(err))
340        else:
341          return _show_login(request, 'accounts/login.html',
342                             {'msg' : "Username %s has been successfully registered." % (user.username)})
343  else:
344    form = forms.GeniUserCreationForm()
345  return direct_to_template(request, 'accounts/register.html', {'form' : form, 'page_top_errors' : page_top_errors })
346 
347
348
349
350
351def _show_login(request, ltemplate, template_dict, form=None):
352    """
353    <Purpose>
354        Show the GENI login form
355
356    <Arguments>
357        request:
358            An HTTP request object to use to populate the form
359           
360        ltemplate:
361           The login template name to use for the login form. Right now
362           this can be one of 'accounts/simplelogin.html' and
363           'accounts/login.html'. They provide different ways of visualizing
364           the login page.
365
366        template_dict:
367           The dictionary of arguments to pass to the template
368
369        form:
370           Either None or the AuthenticationForm to use as a 'form' argument
371           to ltemplate. If form is None, a fresh AuthenticationForm() will be
372           created and used.
373
374    <Exceptions>
375        None.
376
377    <Side Effects>
378        None.
379
380    <Returns>
381        An HTTP response object that represents the login page on
382        success.
383    """
384    if form == None:
385        # initial page load
386        form = AuthenticationForm()
387        # set test cookie, but only once -- remove it on login
388        #if not request.session.test_cookie_worked():
389        request.session.set_test_cookie()
390    template_dict['form'] = form
391    return direct_to_template(request, ltemplate, template_dict)
392 
393 
394 
395 
396
397def login(request):
398  try:
399    # check to see if a user is already logged in. if so, redirect them to profile.
400    user = interface.get_logged_in_user(request)
401  except DoesNotExistError:
402    pass
403  else:
404    return HttpResponseRedirect(reverse("profile"))
405
406  ltemplate = 'accounts/login.html'
407  if request.method == 'POST':
408    form = AuthenticationForm(request.POST)
409   
410    if not request.session.test_cookie_worked():
411      request.session.set_test_cookie()
412      return _show_login(request, ltemplate, {'err' : "Please enable your cookies and try again."}, form)
413
414    if request.POST.has_key('jsenabled') and request.POST['jsenabled'] == 'false':
415      return _show_login(request, ltemplate, {'err' : "Please enable javascript and try again."}, form)
416
417    try:
418      interface.login_user(request, request.POST['username'], request.POST['password'])
419    except DoesNotExistError:
420      return _show_login(request, ltemplate, {'err' : "Wrong username or password."}, form)
421     
422    # only clear out the cookie if we actually authenticate and login ok
423    request.session.delete_test_cookie()
424   
425    return HttpResponseRedirect(reverse("profile"))
426   
427  # request type is GET, show a fresh login page
428  return _show_login(request, ltemplate, {})
429
430
431
432
433
434def logout(request):
435  interface.logout_user(request)
436  # TODO: We should redirect straight to login page
437  return HttpResponseRedirect(reverse("profile"))
438
439
440
441
442
443@login_required
444def help(request):
445  try:
446    user = _validate_and_get_geniuser(request)
447  except LoggedInButFailedGetGeniUserError:
448    return _show_failed_get_geniuser_page(request)
449 
450  return direct_to_template(request, 'control/help.html', {'username': user.username})
451
452
453
454
455
456def accounts_help(request):
457  return direct_to_template(request, 'accounts/help.html', {})
458
459
460
461@login_required
462def mygeni(request):
463  try:
464    user = _validate_and_get_geniuser(request)
465  except LoggedInButFailedGetGeniUserError:
466    return _show_failed_get_geniuser_page(request)
467 
468  total_vessel_credits = interface.get_total_vessel_credits(user)
469  num_acquired_vessels = len(interface.get_acquired_vessels(user))
470  avail_vessel_credits = interface.get_available_vessel_credits(user)
471 
472  if num_acquired_vessels > total_vessel_credits:
473    percent_total_used = 100
474    over_vessel_credits = num_acquired_vessels - total_vessel_credits
475  else:
476    percent_total_used = int((num_acquired_vessels * 1.0 / total_vessel_credits * 1.0) * 100.0)
477    over_vessel_credits = 0
478 
479  # total_vessel_credits, percent_total_used, avail_vessel_credits
480  return direct_to_template(request, 'control/mygeni.html',
481                            {'username' : user.username,
482                             'total_vessel_credits' : total_vessel_credits,
483                             'used_vessel_credits' : num_acquired_vessels,
484                             'percent_total_used' : percent_total_used,
485                             'avail_vessel_credits' : avail_vessel_credits,
486                             'over_vessel_credits' : over_vessel_credits})
487
488
489
490
491
492@login_required
493def myvessels(request, get_form=False, action_summary="", action_detail="", remove_summary=""):
494  try:
495    user = _validate_and_get_geniuser(request)
496  except LoggedInButFailedGetGeniUserError:
497    return _show_failed_get_geniuser_page(request)
498 
499  # get_form of None means don't show the form to acquire vessels.
500  if interface.get_available_vessel_credits(user) == 0:
501    get_form = None
502  elif get_form is False:
503    get_form = forms.gen_get_form(user)
504
505  # shared vessels that are used by others but which belong to this user (TODO)
506  shvessels = []
507
508  # this user's used vessels
509  my_vessels_raw = interface.get_acquired_vessels(user)
510  my_vessels = interface.get_vessel_infodict_list(my_vessels_raw)
511 
512  # this user's number of donations, max vessels, total vessels and free credits
513  my_donations = interface.get_donations(user)
514  my_max_vessels = interface.get_available_vessel_credits(user) 
515  my_free_vessel_credits = interface.get_free_vessel_credits_amount(user)
516  my_total_vessel_credits = interface.get_total_vessel_credits(user)
517
518  for vessel in my_vessels:
519    if vessel["expires_in_seconds"] <= 0:
520      # We shouldn't ever get here, but just in case, let's handle it.
521      vessel["expires_in"] = "Expired"
522    else:
523      days = vessel["expires_in_seconds"] / (3600 * 24)
524      hours = vessel["expires_in_seconds"] / 3600 % 24
525      minutes = vessel["expires_in_seconds"] / 60 % 60
526      vessel["expires_in"] = "%dd %dh %dm" % (days, hours, minutes)
527
528  # return the used resources page constructed from a template
529  return direct_to_template(request, 'control/myvessels.html',
530                            {'username' : user.username,
531                             'num_vessels' : len(my_vessels),
532                             'my_vessels' : my_vessels,
533                             'sh_vessels' : shvessels,
534                             'get_form' : get_form,
535                             'action_summary' : action_summary,
536                             'action_detail' : action_detail,
537                             'my_donations' : len(my_donations),
538                             'my_max_vessels' : my_max_vessels, 
539                             'free_vessel_credits' : my_free_vessel_credits,
540                             'total_vessel_credits' : my_total_vessel_credits,
541                             'remove_summary' : remove_summary})
542
543
544
545
546
547@login_required
548def getdonations(request):
549  try:
550    user = _validate_and_get_geniuser(request)
551  except LoggedInButFailedGetGeniUserError:
552    return _show_failed_get_geniuser_page(request)
553 
554  domain = "https://" + request.get_host()
555 
556  return direct_to_template(request, 'control/getdonations.html',
557                            {'username' : user.username,
558                             'domain' : domain})
559
560
561
562
563
564@login_required
565def get_resources(request):
566  try:
567    user = _validate_and_get_geniuser(request)
568  except LoggedInButFailedGetGeniUserError:
569    return _show_failed_get_geniuser_page(request)
570 
571  # the request must be via POST. if not, bounce user back to My Vessels page
572  if not request.method == 'POST':
573    return myvessels(request)
574 
575  # try and grab form from POST. if it can't, bounce user back to My Vessels page
576  get_form = forms.gen_get_form(user, request.POST)
577 
578  action_summary = ""
579  action_detail = ""
580  keep_get_form = False
581 
582  if get_form.is_valid():
583    vessel_num = get_form.cleaned_data['num']
584    vessel_type = get_form.cleaned_data['env']
585   
586    try:
587      acquired_vessels = interface.acquire_vessels(user, vessel_num, vessel_type)
588    except UnableToAcquireResourcesError, err:
589      action_summary = "Unable to acquire vessels at this time."
590      action_detail += str(err)
591      keep_get_form = True
592    except InsufficientUserResourcesError:
593      action_summary = "Unable to acquire vessels: you do not have enough vessel credits to fulfill this request."
594      keep_get_form = True
595  else:
596    keep_get_form = True
597 
598  if keep_get_form == True:
599    # return the original get_form, since the form wasn't valid (or there were errors)
600    return myvessels(request, get_form, action_summary=action_summary, action_detail=action_detail)
601  else:
602    # return a My Vessels page with the updated vessels/vessel acquire details/errors
603    return myvessels(request, False, action_summary=action_summary, action_detail=action_detail)
604 
605 
606 
607 
608
609@login_required
610def del_resource(request):
611  try:
612    user = _validate_and_get_geniuser(request)
613  except LoggedInButFailedGetGeniUserError:
614    return _show_failed_get_geniuser_page(request)
615 
616  # the request must be via POST. if not, bounce user back to My Vessels page
617  if not request.method == 'POST':
618    return myvessels(request)
619
620  if not request.POST['handle']:
621    return myvessels(request)
622 
623  # vessel_handle needs to be a list (even though we only add one handle),
624  # since get_vessel_list expects a list.
625  vessel_handle = []
626  vessel_handle.append(request.POST['handle'])
627  remove_summary = ""
628 
629  try:
630    # convert handle to vessel
631    vessel_to_release = interface.get_vessel_list(vessel_handle)
632  except DoesNotExistError:
633    remove_summary = "Unable to remove vessel. The vessel you are trying to remove does not exist."
634  except InvalidRequestError, err:
635    remove_summary = "Unable to remove vessel. " + str(err)
636  else:
637    try:
638      interface.release_vessels(user, vessel_to_release)
639    except InvalidRequestError, err:
640      remove_summary = "Unable to remove vessel. The vessel does not belong"
641      remove_summary += " to you any more (maybe it expired?). " + str(err)
642 
643  return myvessels(request, remove_summary=remove_summary)
644
645
646
647
648
649
650@login_required
651def del_all_resources(request):
652  try:
653    user = _validate_and_get_geniuser(request)
654  except LoggedInButFailedGetGeniUserError:
655    return _show_failed_get_geniuser_page(request)
656 
657  # the request must be via POST. if not, bounce user back to My Vessels page
658  if not request.method == 'POST':
659    return myvessels(request)
660 
661  remove_summary = ""
662
663  try:
664    interface.release_all_vessels(user)
665  except InvalidRequestError, err:
666    remove_summary = "Unable to release all vessels: " + str(err)
667 
668  return myvessels(request, remove_summary=remove_summary)
669
670
671
672
673
674
675@login_required
676def renew_resource(request):
677  try:
678    user = _validate_and_get_geniuser(request)
679  except LoggedInButFailedGetGeniUserError:
680    return _show_failed_get_geniuser_page(request)
681 
682  # The request must be via POST. If not, bounce user back to My Vessels page.
683  if not request.method == 'POST':
684    return myvessels(request)
685 
686  if not request.POST.get('handle', ''):
687    return myvessels(request)
688 
689  action_summary = ""
690  action_detail = ""
691 
692  try:
693    # Convert handle to vessel object.
694    # Raises a DoesNotExistError if the vessel does not exist. This is more
695    # likely to be an error than an expected case, so we let it bubble up.
696    vessel_handle_list = [request.POST['handle']]
697    vessel_to_renew = interface.get_vessel_list(vessel_handle_list)
698  except DoesNotExistError:
699    action_summary = "Unable to renew vessel: The vessel you are trying to delete does not exist."
700  except InvalidRequestError, err:
701    action_summary = "Unable to renew vessel."
702    action_detail += str(err)
703  else:
704    try:
705      interface.renew_vessels(user, vessel_to_renew)
706    except InvalidRequestError, err:
707      action_summary = "Unable to renew vessel: " + str(err)
708    except InsufficientUserResourcesError, err:
709      action_summary = "Unable to renew vessel: you are currently over your"
710      action_summary += " vessel credit limit."
711      action_detail += str(err)
712 
713  return myvessels(request, False, action_summary=action_summary, action_detail=action_detail)
714
715
716
717
718
719@login_required
720def renew_all_resources(request):
721  try:
722    user = _validate_and_get_geniuser(request)
723  except LoggedInButFailedGetGeniUserError:
724    return _show_failed_get_geniuser_page(request)
725 
726  # The request must be via POST. If not, bounce user back to My Vessels page.
727  if not request.method == 'POST':
728    return myvessels(request)
729 
730  action_summary = ""
731  action_detail = ""
732 
733  try:
734    interface.renew_all_vessels(user)
735  except InvalidRequestError, err:
736    action_summary = "Unable to renew vessels: " + str(err)
737  except InsufficientUserResourcesError, err:
738    action_summary = "Unable to renew vessels: you are currently over your"
739    action_summary += " vessel credit limit."
740    action_detail += str(err)
741 
742  return myvessels(request, False, action_summary=action_summary, action_detail=action_detail)
743
744
745
746
747
748@login_required
749def change_key(request):
750  try:
751    user = _validate_and_get_geniuser(request)
752  except LoggedInButFailedGetGeniUserError:
753    return _show_failed_get_geniuser_page(request)
754  info = ""
755  if request.method == 'GET':
756    return direct_to_template(request, 'control/change_key.html',
757                              {'username' : user.username,
758                               'error_msg' : ""})
759
760  # This is a POST, so figure out if a file was uploaded or if we are supposed
761  # to generate a new key for the user.
762  if request.POST.get('generate', False):
763    interface.change_user_keys(user, pubkey=None)
764    msg = "Your new keys have been generated. You should download them now."
765    return profile(request, msg)
766   
767  else:
768    file = request.FILES.get('pubkey', None)
769    if file is None:
770      msg = "You didn't select a public key file to upload." 
771      return profile(request, info, msg)
772      #return direct_to_template(request, 'control/change_key.html',
773      #                          {'username' : user.username,
774      #                           'error_msg' : msg})
775   
776    if file.size == 0 or file.size > forms.MAX_PUBKEY_UPLOAD_SIZE:
777      msg = "Invalid file uploaded. The file size limit is " 
778      msg += str(forms.MAX_PUBKEY_UPLOAD_SIZE) + " bytes."
779      return profile(request, info, msg) 
780      #direct_to_template(request, 'control/change_key.html',
781      #                          {'username' : user.username,
782      #                           'error_msg' : msg})
783   
784    pubkey = file.read()
785   
786    try:
787      validations.validate_pubkey_string(pubkey)
788    except ValidationError:
789      msg = "Invalid public key uploaded."
790      return profile(request, info, msg)
791      #direct_to_template(request, 'control/change_key.html',
792      #                          {'username' : user.username,
793      #                           'error_msg' : msg})
794   
795    # If we made it here, the uploaded key is good.
796    interface.change_user_keys(user, pubkey=pubkey)
797    msg = "Your public key has been successfully changed."
798    return profile(request, msg)
799
800
801
802
803
804@login_required
805def api_info(request):
806  try:
807    user = _validate_and_get_geniuser(request)
808  except LoggedInButFailedGetGeniUserError:
809    return _show_failed_get_geniuser_page(request)
810 
811  if request.method == 'GET':
812    return direct_to_template(request, 'control/api_info.html',
813                              {'username' : user.username,
814                               'api_key' : user.api_key,
815                               'msg' : ""})
816
817  # This is a POST, so it should be generation of an API key.
818  if not request.POST.get('generate_api_key', False):
819    msg = "Sorry, we didn't understand your request."
820    return profile(request, info, msg) 
821    #direct_to_template(request, 'control/api_info.html',
822    #                          {'username' : user.username,
823    #                           'api_key' : user.api_key,
824    #                           'msg' : msg})
825   
826  interface.regenerate_api_key(user)
827  msg = "Your API key has been regenerated. Your old one will no longer work."
828  msg += " You should update any places you are using the API key"
829  msg += " (e.g. in programs using the XML-RPC client)."
830  return profile(request,msg)
831  #direct_to_template(request, 'control/api_info.html',
832  #                         {'username' : user.username,
833  #                           'api_key' : user.api_key,
834  #                           'msg' : msg})
835
836
837
838
839
840@log_function_call
841@require_POST
842@login_required
843def del_priv(request):
844  try:
845    user = _validate_and_get_geniuser(request)
846  except LoggedInButFailedGetGeniUserError:
847    return _show_failed_get_geniuser_page(request)
848 
849  if user.user_privkey == "":
850    msg = "Your private key has already been deleted."
851  else:
852    interface.delete_private_key(user)
853    msg = "Your private key has been deleted."
854  return profile(request, msg)
855
856
857
858
859
860@log_function_call
861@login_required
862def priv_key(request):
863  try:
864    user = _validate_and_get_geniuser(request)
865  except LoggedInButFailedGetGeniUserError:
866    return _show_failed_get_geniuser_page(request)
867 
868  response = HttpResponse(user.user_privkey, mimetype='text/plain')
869  response['Content-Disposition'] = 'attachment; filename=' + str(user.username) + '.privatekey'
870  return response
871
872
873
874
875
876@log_function_call
877@login_required
878def pub_key(request):
879  try:
880    user = _validate_and_get_geniuser(request)
881  except LoggedInButFailedGetGeniUserError:
882    return _show_failed_get_geniuser_page(request)
883 
884  response = HttpResponse(user.user_pubkey, mimetype='text/plain')
885  response['Content-Disposition'] = 'attachment; filename=' + str(user.username) + '.publickey'
886  return response
887
888
889
890
891
892def download(request, username):
893  validuser = True
894  try:
895    # validate that this username actually exists
896    user = interface.get_user_for_installers(username)
897  except DoesNotExistError:
898    validuser = False
899
900  templatedict = {}
901  templatedict['username'] = username
902  templatedict['validuser'] = validuser
903  templatedict['domain'] = "https://" + request.get_host()
904  # I need to build a URL for android to download the installer from.   (The
905  # same installer is downloaded from the Google Play store for all users.)
906  # The URL is escaped twice (ask Akos why) and inserted in the referrer
907  # information in the URL.   
908  #templatedict['android_installer_link'] = urllib.quote(urllib.quote(domain,safe=''),safe='')
909
910  return direct_to_template(request, 'download/installers.html', templatedict)
911
912
913
914
915
916def build_android_installer(request, username):
917  """
918  <Purpose>
919    Allows the user to download a Android distribution of Seattle that will
920    donate resources to user with 'username'.
921 
922  <Arguments>
923    request:
924      Django HttpRequest object
925       
926    username:
927      A string representing the GENI user to which the installer will donate
928      resources.
929 
930  <Exceptions>
931    None
932 
933  <Side Effects>
934    None
935 
936  <Returns>
937    On failure, returns an HTTP response with a description of the error. On
938    success, redirects the user to download the installer.
939  """
940 
941  success, return_value = _build_installer(username, "android")
942 
943  if not success:
944    error_response = return_value
945    return error_response
946 
947  installer_url = return_value
948  return HttpResponseRedirect(installer_url)
949
950
951
952
953
954def build_win_installer(request, username):
955  """
956  <Purpose>
957    Allows the user to download a Windows distribution of Seattle that will
958    donate resources to user with 'username'.
959 
960  <Arguments>
961    request:
962      Django HttpRequest object
963       
964    username:
965      A string representing the GENI user to which the installer will donate
966      resources.
967 
968  <Exceptions>
969    None
970 
971  <Side Effects>
972    None
973 
974  <Returns>
975    On failure, returns an HTTP response with a description of the error. On
976    success, redirects the user to download the installer.
977  """
978 
979  success, return_value = _build_installer(username, "windows")
980 
981  if not success:
982    error_response = return_value
983    return error_response
984 
985  installer_url = return_value
986  return HttpResponseRedirect(installer_url)
987
988
989
990
991
992def build_linux_installer(request, username):
993  """
994  <Purpose>
995    Allows the user to download a Linux distribution of Seattle that will
996    donate resources to user with 'username'.
997
998  <Arguments>
999    request:
1000      Django HttpRequest object
1001
1002    username:
1003      A string representing the GENI user to which the installer will donate
1004      resources.
1005
1006  <Exceptions>
1007    None
1008
1009  <Side Effects>
1010    None
1011
1012  <Returns>
1013    On failure, returns an HTTP response with a description of the error. On
1014    success, redirects the user to download the installer.
1015  """
1016
1017  success, return_value = _build_installer(username, "linux")
1018
1019  if not success:
1020    error_response = return_value
1021    return error_response
1022
1023  installer_url = return_value
1024  return HttpResponseRedirect(installer_url)
1025
1026
1027
1028
1029
1030def build_mac_installer(request, username):
1031  """
1032  <Purpose>
1033    Allows the user to download a Mac distribution of Seattle that will
1034    donate resources to user with 'username'.
1035
1036  <Arguments>
1037    request:
1038      Django HttpRequest object
1039
1040    username:
1041      A string representing the GENI user to which the installer will donate
1042      resources.
1043
1044  <Exceptions>
1045    None
1046
1047  <Side Effects>
1048    None
1049
1050  <Returns>
1051    On failure, returns an HTTP response with a description of the error. On
1052    success, redirects the user to download the installer.
1053  """
1054
1055  success, return_value = _build_installer(username, "mac")
1056
1057  if not success:
1058    error_response = return_value
1059    return error_response
1060
1061  installer_url = return_value
1062  return HttpResponseRedirect(installer_url)
1063
1064
1065
1066
1067
1068def _build_installer(username, platform):
1069  """
1070  <Purpose>
1071    Builds an installer for the given platform that will donate resources to
1072    the user with the given username.
1073 
1074  <Arguments>
1075    username:
1076      A string representing the GENI user to which the installer will donate
1077      resources.
1078     
1079    platform:
1080      A string representing the platform for which to build the installer.
1081      Options include 'windows', 'linux', or 'mac'.
1082 
1083  <Exceptions>
1084    None
1085 
1086  <Side Effects>
1087    None
1088 
1089  <Returns>
1090    On success, returns (True, installer_url) where installer_url is URL from
1091    which the installer may be downloaded.
1092   
1093    On failure, returns (False, error_response) where error_repsponse is an
1094    HttpResponse which specifies what went wrong.
1095  """
1096  try:
1097    user = interface.get_user_for_installers(username)
1098    username = user.username
1099  except DoesNotExistError:
1100    error_response = HttpResponse("Couldn't get user.")
1101    return False, error_response
1102
1103  try:
1104    xmlrpc_proxy = xmlrpclib.ServerProxy(settings.SEATTLECLEARINGHOUSE_INSTALLER_BUILDER_XMLRPC)
1105   
1106    vessel_list = [{'percentage': 80, 'owner': 'owner', 'users': ['user']}]
1107   
1108    user_data = {
1109      'owner': {'public_key': user.donor_pubkey},
1110      'user': {'public_key': ACCEPTDONATIONS_STATE_PUBKEY},
1111    }
1112   
1113    build_results = xmlrpc_proxy.build_installers(vessel_list, user_data)
1114  except:
1115    error_response = HttpResponse("Failed to build installer.")
1116    return False, error_response
1117
1118  installer_url = build_results['installers'][platform]
1119  return True, installer_url
1120
1121
1122
1123
1124
1125def donations_help(request, username):
1126  return direct_to_template(request, 'download/help.html', {'username' : username})
1127
1128
1129
1130
1131
1132def _validate_and_get_geniuser(request):
1133  try:
1134    user = interface.get_logged_in_user(request)
1135  except DoesNotExistError:
1136    # Failed to get GeniUser record, but user is logged in
1137    raise LoggedInButFailedGetGeniUserError
1138  return user
1139
1140
1141
1142
1143
1144@log_function_call_without_return
1145@login_required
1146def new_auto_register_user(request):
1147  msg = "Your account has been succesfully created. "
1148  msg += "If you would like to login without OpenID/OAuth please change your password now."
1149  return profile(request,msg)
1150
1151
1152
1153
1154
1155def _show_failed_get_geniuser_page(request):
1156  err = "Sorry, we can't display the page you requested. "
1157  err += "If you are logged in as an administrator, you'll need to logout, and login with a Seattle Clearinghouse account. "
1158  err += "If you aren't logged in as an administrator, then this is a bug. Please contact us!"
1159  return _show_login(request, 'accounts/login.html', {'err' : err})
Note: See TracBrowser for help on using the browser.