import logging
import datetime
+import time
from urllib import quote
-from urlparse import urlsplit, urlunsplit
-from functools import wraps
+from urlparse import urlsplit, urlunsplit, urlparse
from datetime import tzinfo, timedelta
-from django.http import HttpResponse, urlencode
+from django.http import HttpResponse, HttpResponseBadRequest, urlencode
from django.template import RequestContext
-from django.contrib.sites.models import Site
from django.utils.translation import ugettext as _
-from django.contrib.auth import login, authenticate
+from django.contrib.auth import authenticate
from django.core.urlresolvers import reverse
+from django.core.exceptions import ValidationError
-from astakos.im.models import AstakosUser, Invitation
-from astakos.im.settings import INVITATIONS_PER_LEVEL, COOKIE_NAME, COOKIE_DOMAIN, COOKIE_SECURE, FORCE_PROFILE_UPDATE
+from astakos.im.models import AstakosUser, Invitation, ApprovalTerms
+from astakos.im.settings import (
+ INVITATIONS_PER_LEVEL, COOKIE_NAME, COOKIE_DOMAIN, COOKIE_SECURE,
+ FORCE_PROFILE_UPDATE, LOGGING_LEVEL
+)
+from astakos.im.functions import login
logger = logging.getLogger(__name__)
return d.replace(tzinfo=UTC()).isoformat()
-def get_or_create_user(email, realname='', first_name='', last_name='', affiliation='', level=0, provider='local', password=''):
- """Find or register a user into the internal database
- and issue a token for subsequent requests.
- """
- user, created = AstakosUser.objects.get_or_create(email=email,
- defaults={
- 'password':password,
- 'affiliation':affiliation,
- 'level':level,
- 'invitations':INVITATIONS_PER_LEVEL.get(level, 0),
- 'provider':provider,
- 'realname':realname,
- 'first_name':first_name,
- 'last_name':last_name
- })
- if created:
- user.renew_token()
- user.save()
- logger.info('Created user %s', user)
-
- return user
+def epoch(datetime):
+ return int(time.mktime(datetime.timetuple())*1000)
def get_context(request, extra_context={}, **kwargs):
if not extra_context:
"""
Returns the invitation identified by the ``code``.
- Raises Invitation.DoesNotExist and Exception if the invitation is consumed
+ Raises ValueError if the invitation is consumed or there is another account
+ associated with this email.
"""
code = request.GET.get('code')
if request.method == 'POST':
code = request.POST.get('code')
if not code:
- if 'invitation_code' in request.session:
- code = request.session.pop('invitation_code')
- if not code:
return
invitation = Invitation.objects.get(code = code)
if invitation.is_consumed:
raise ValueError(_('Invitation is used'))
- try:
- AstakosUser.objects.get(email = invitation.username)
+ if reserved_email(invitation.username):
raise ValueError(_('Email: %s is reserved' % invitation.username))
- except AstakosUser.DoesNotExist:
- pass
return invitation
+def restrict_next(url, domain=None, allowed_schemes=()):
+ """
+ Return url if having the supplied ``domain`` (if present) or one of the ``allowed_schemes``.
+ Otherwise return None.
+
+ >>> print restrict_next('/im/feedback', '.okeanos.grnet.gr')
+ /im/feedback
+ >>> print restrict_next('pithos.okeanos.grnet.gr/im/feedback', '.okeanos.grnet.gr')
+ pithos.okeanos.grnet.gr/im/feedback
+ >>> print restrict_next('https://pithos.okeanos.grnet.gr/im/feedback', '.okeanos.grnet.gr')
+ https://pithos.okeanos.grnet.gr/im/feedback
+ >>> print restrict_next('pithos://127.0.0,1', '.okeanos.grnet.gr')
+ None
+ >>> print restrict_next('pithos://127.0.0,1', '.okeanos.grnet.gr', allowed_schemes=('pithos'))
+ pithos://127.0.0,1
+ >>> print restrict_next('node1.example.com', '.okeanos.grnet.gr')
+ None
+ >>> print restrict_next('//node1.example.com', '.okeanos.grnet.gr')
+ None
+ >>> print restrict_next('https://node1.example.com', '.okeanos.grnet.gr')
+ None
+ >>> print restrict_next('https://node1.example.com')
+ https://node1.example.com
+ >>> print restrict_next('//node1.example.com')
+ //node1.example.com
+ >>> print restrict_next('node1.example.com')
+ node1.example.com
+ """
+ if not url:
+ return
+ parts = urlparse(url, scheme='http')
+ if not parts.netloc:
+ # fix url if does not conforms RFC 1808
+ url = '//%s' % url
+ parts = urlparse(url, scheme='http')
+ # TODO more scientific checks?
+ if not parts.netloc: # internal url
+ return url
+ elif not domain:
+ return url
+ elif parts.netloc.endswith(domain):
+ return url
+ elif parts.scheme in allowed_schemes:
+ return url
+
def prepare_response(request, user, next='', renew=False):
"""Return the unique username and the token
as 'X-Auth-User' and 'X-Auth-Token' headers,
expired, if the 'renew' parameter is present
or user has not a valid token.
"""
-
renew = renew or (not user.auth_token)
renew = renew or (user.auth_token_expires and user.auth_token_expires < datetime.datetime.now())
if renew:
user.renew_token()
- user.save()
+ try:
+ user.save()
+ except ValidationError, e:
+ return HttpResponseBadRequest(e)
+
+ next = restrict_next(next, domain=COOKIE_DOMAIN)
if FORCE_PROFILE_UPDATE and not user.is_verified and not user.is_superuser:
params = ''
user = authenticate(email=user.email, auth_token=user.auth_token)
login(request, user)
set_cookie(response, user)
+ request.session.set_expiry(user.auth_token_expires)
if not next:
next = reverse('astakos.im.views.index')
response.set_cookie(COOKIE_NAME, value=cookie_value,
expires=expire_fmt, path='/',
domain=COOKIE_DOMAIN, secure=COOKIE_SECURE)
+ msg = 'Cookie [expiring %s] set for %s' % (user.auth_token_expires, user.email)
+ logger._log(LOGGING_LEVEL, msg, [])
+
+class lazy_string(object):
+ def __init__(self, function, *args, **kwargs):
+ self.function=function
+ self.args=args
+ self.kwargs=kwargs
+
+ def __str__(self):
+ if not hasattr(self, 'str'):
+ self.str=self.function(*self.args, **self.kwargs)
+ return self.str
+
+def reverse_lazy(*args, **kwargs):
+ return lazy_string(reverse, *args, **kwargs)
+
+def reserved_email(email):
+ return AstakosUser.objects.filter(email = email).count() != 0
+
+def get_query(request):
+ try:
+ return request.__getattribute__(request.method)
+ except AttributeError:
+ return {}
\ No newline at end of file