commit 0e79735c7461e9657eb6c124079712bc949128a9
Author: Kostas Papadimitriou <kpap@grnet.gr>
Date:   Wed Jan 23 20:33:24 2013 +0200

    Several auth providers fixes/improvements
    
    - Allow third party signup if existing account with the same third party
      identifier is not yet verified. Subsequent registrations will replace
      previous ones (pending/unverified accounts will be removed).
    - Merge common third party code in target/__init__.py
    - Be able to identify if third party signup comes from login page (or
      user explicitly requested to signup)

diff --git a/snf-astakos-app/astakos/im/auth_providers.py b/snf-astakos-app/astakos/im/auth_providers.py
index fd3a88b..c5e41ac 100644
--- a/snf-astakos-app/astakos/im/auth_providers.py
+++ b/snf-astakos-app/astakos/im/auth_providers.py
@@ -99,8 +99,25 @@ class AuthProvider(object):
     def add_url(self):
         return reverse(self.login_view)
 
-    def __init__(self, user=None):
+    @property
+    def provider_details(self):
+        if self.user:
+            if self.identifier:
+                self._provider_details = \
+                    self.user.get_auth_providers().get(module=self.module,
+                                                       identifier=self.identifier).__dict__
+            else:
+                self._provider_details = self.user.get(module=self.module).__dict__
+        return self._provider_details
+
+    def __init__(self, user=None, identifier=None, provider_details=None):
         self.user = user
+        self.identifier = identifier
+
+        self._provider_details = None
+        if provider_details:
+            self._provider_details = provider_details
+
         for tpl in ['login_prompt', 'login', 'signup_prompt']:
             tpl_name = '%s_%s' % (tpl, 'template')
             override = self.get_setting(tpl_name)
@@ -254,12 +271,12 @@ class LinkedInAuthProvider(AuthProvider):
     login_prompt_template = 'im/auth/third_party_provider_generic_login_prompt.html'
 
 
-def get_provider(id, user_obj=None, default=None):
+def get_provider(id, user_obj=None, default=None, identifier=None, provider_details={}):
     """
     Return a provider instance from the auth providers registry.
     """
     if not id in PROVIDERS:
         raise Exception('Invalid auth provider requested "%s"' % id)
 
-    return PROVIDERS.get(id, default)(user_obj)
+    return PROVIDERS.get(id, default)(user_obj, identifier, provider_details)
 
diff --git a/snf-astakos-app/astakos/im/messages.py b/snf-astakos-app/astakos/im/messages.py
index 43578b1..9ab691b 100644
--- a/snf-astakos-app/astakos/im/messages.py
+++ b/snf-astakos-app/astakos/im/messages.py
@@ -207,6 +207,7 @@ AUTH_PROVIDER_LOGIN_TO_ADD                   =   "The new login method will be a
 AUTH_PROVIDER_INVALID_LOGIN                  =   "No account exists."
 AUTH_PROVIDER_REQUIRED                       =   "%(provider)s login method is required. Add one from your profile page."
 AUTH_PROVIDER_CANNOT_CHANGE_PASSWORD         =   "Changing password is not supported."
+AUTH_PROVIDER_SIGNUP_FROM_LOGIN              =   None
 
 EXISTING_EMAIL_THIRD_PARTY_NOTIFICATION      =   "You can add '%s' login method to your existing account from your " \
                                                  " <a href='%s'>profile page</a>"
diff --git a/snf-astakos-app/astakos/im/models.py b/snf-astakos-app/astakos/im/models.py
index c5887a5..2ea7028 100644
--- a/snf-astakos-app/astakos/im/models.py
+++ b/snf-astakos-app/astakos/im/models.py
@@ -601,6 +601,7 @@ class AstakosUser(User):
         if 'identifier' in kwargs:
             try:
                 # provider with specified params already exist
+                print "LALALALA", include_unverified
                 if not include_unverified:
                     kwargs['user__email_verified'] = True
                 existing_user = AstakosUser.objects.get_auth_provider_user(provider,
diff --git a/snf-astakos-app/astakos/im/target/__init__.py b/snf-astakos-app/astakos/im/target/__init__.py
index b145b68..130ad7b 100644
--- a/snf-astakos-app/astakos/im/target/__init__.py
+++ b/snf-astakos-app/astakos/im/target/__init__.py
@@ -46,6 +46,19 @@ from astakos.im.util import prepare_response, get_context
 from astakos.im.views import requires_anonymous, render_response
 
 
+def init_third_party_session(request):
+    params = dict(request.GET.items())
+    request.session['third_party_request_params'] = params
+
+
+def get_third_party_session_params(request):
+    if 'third_party_request_params' in request.session:
+        params = request.session['third_party_request_params']
+        del request.session['third_party_request_params']
+        return params
+    return {}
+
+
 def add_pending_auth_provider(request, third_party_token):
     if third_party_token:
         # use requests to assign the account he just authenticated with with
@@ -58,11 +71,9 @@ def add_pending_auth_provider(request, third_party_token):
 
 
 def get_pending_key(request):
-    third_party_token = get_query(request).get('key', False)
-    if not third_party_token:
-        third_party_token = request.session.get('pending_key', None)
-        if third_party_token:
-          del request.session['pending_key']
+    third_party_token = get_query(request).get('key', request.session.get('pending_key', False))
+    if 'pending_key' in request.session:
+        del request.session['pending_key']
     return third_party_token
 
 
@@ -115,8 +126,8 @@ def handle_third_party_signup(request, userid, provider_module, third_party_key,
 
 
 def handle_third_party_login(request, provider_module, identifier,
-                             third_party_key=None, provider_info=None,
-                             affiliation=None):
+                             provider_info=None, affiliation=None,
+                             third_party_key=None):
 
     if not provider_info:
         provider_info = {}
@@ -124,13 +135,13 @@ def handle_third_party_login(request, provider_module, identifier,
     if not affiliation:
         affiliation = provider_module.title()
 
-    if not third_party_key:
-        third_party_key = get_pending_key(request)
-
     next_redirect = request.GET.get('next', request.session.get('next_url', None))
     if 'next_url' in request.session:
         del request.session['next_url']
 
+    third_party_request_params = get_third_party_session_params(request)
+    from_login = third_party_request_params.get('from_login', False)
+
     # an existing user accessed the view
     if request.user.is_authenticated():
 
@@ -153,10 +164,22 @@ def handle_third_party_login(request, provider_module, identifier,
         return HttpResponseRedirect(reverse('edit_profile'))
 
     # astakos user exists ?
-    user = AstakosUser.objects.get_auth_provider_user(
-        provider_module,
-        identifier=identifier
-    )
+    try:
+        user = AstakosUser.objects.get_auth_provider_user(
+            provider_module,
+            identifier=identifier,
+            user__email_verified=True,
+        )
+    except AstakosUser.DoesNotExist:
+        # TODO: add a message ? redirec to login ?
+        if astakos_messages.AUTH_PROVIDER_SIGNUP_FROM_LOGIN:
+            messages.warning(request,
+                             astakos_messages.AUTH_PROVIDER_SIGNUP_FROM_LOGIN)
+        raise
+
+    if not third_party_key:
+        third_party_key = get_pending_key(request)
+
     if user.is_active:
         # authenticate user
         response = prepare_response(request,
diff --git a/snf-astakos-app/astakos/im/target/google.py b/snf-astakos-app/astakos/im/target/google.py
index f4d591a..c00c73e 100644
--- a/snf-astakos-app/astakos/im/target/google.py
+++ b/snf-astakos-app/astakos/im/target/google.py
@@ -55,7 +55,7 @@ from astakos.im.activation_backends import get_backend, SimpleBackend
 from astakos.im import settings
 from astakos.im import auth_providers
 from astakos.im.target import add_pending_auth_provider, get_pending_key, \
-    handle_third_party_signup, handle_third_party_login
+    handle_third_party_signup, handle_third_party_login, init_third_party_session
 
 import logging
 import time
@@ -89,6 +89,7 @@ def get_redirect_uri():
 @requires_auth_provider('google', login=True)
 @require_http_methods(["GET", "POST"])
 def login(request):
+    init_third_party_session(request)
     params = {
         'scope': token_scope,
         'response_type': 'code',
diff --git a/snf-astakos-app/astakos/im/target/linkedin.py b/snf-astakos-app/astakos/im/target/linkedin.py
index 9c4f035..07f1d6e 100644
--- a/snf-astakos-app/astakos/im/target/linkedin.py
+++ b/snf-astakos-app/astakos/im/target/linkedin.py
@@ -55,7 +55,7 @@ from astakos.im.activation_backends import get_backend, SimpleBackend
 from astakos.im import settings
 from astakos.im import auth_providers
 from astakos.im.target import add_pending_auth_provider, get_pending_key, \
-    handle_third_party_signup, handle_third_party_login
+    handle_third_party_signup, handle_third_party_login, init_third_party_session
 
 import astakos.im.messages as astakos_messages
 
@@ -77,6 +77,7 @@ authenticate_url       = 'https://www.linkedin.com/uas/oauth/authorize'
 @requires_auth_provider('linkedin', login=True)
 @require_http_methods(["GET", "POST"])
 def login(request):
+    init_third_party_session(request)
     resp, content = client.request(request_token_url, "GET")
     if resp['status'] != '200':
         messages.error(request, 'Invalid linkedin response')
diff --git a/snf-astakos-app/astakos/im/target/shibboleth.py b/snf-astakos-app/astakos/im/target/shibboleth.py
index 6c071b7..236f54b 100644
--- a/snf-astakos-app/astakos/im/target/shibboleth.py
+++ b/snf-astakos-app/astakos/im/target/shibboleth.py
@@ -55,7 +55,7 @@ from astakos.im.activation_backends import get_backend, SimpleBackend
 from astakos.im import auth_providers
 from astakos.im import settings
 from astakos.im.target import add_pending_auth_provider, get_pending_key, \
-    handle_third_party_signup, handle_third_party_login
+    handle_third_party_signup, handle_third_party_login, init_third_party_session
 
 import astakos.im.messages as astakos_messages
 import logging
@@ -82,6 +82,7 @@ def login(
     template='im/third_party_check_local.html',
     extra_context=None):
 
+    init_third_party_session(request)
     extra_context = extra_context or {}
 
     tokens = request.META
diff --git a/snf-astakos-app/astakos/im/target/twitter.py b/snf-astakos-app/astakos/im/target/twitter.py
index 79b762d..07bed99 100644
--- a/snf-astakos-app/astakos/im/target/twitter.py
+++ b/snf-astakos-app/astakos/im/target/twitter.py
@@ -56,7 +56,7 @@ from astakos.im.activation_backends import get_backend, SimpleBackend
 from astakos.im import settings
 from astakos.im import auth_providers
 from astakos.im.target import add_pending_auth_provider, get_pending_key, \
-    handle_third_party_signup, handle_third_party_login
+    handle_third_party_signup, handle_third_party_login, init_third_party_session
 
 import astakos.im.messages as astakos_messages
 
@@ -77,6 +77,7 @@ authenticate_url = 'http://twitter.com/oauth/authenticate'
 @requires_auth_provider('twitter', login=True)
 @require_http_methods(["GET", "POST"])
 def login(request):
+    init_third_party_session(request)
     force_login = request.GET.get('force_login',
                                   settings.TWITTER_AUTH_FORCE_LOGIN)
     resp, content = client.request(request_token_url, "GET")
@@ -142,7 +143,7 @@ def authenticated(
 
 
     try:
-        return handle_third_party_login(request, 'google', userid,
+        return handle_third_party_login(request, 'twitter', userid,
                                         provider_info, affiliation)
     except AstakosUser.DoesNotExist, e:
         third_party_key = get_pending_key(request)
diff --git a/snf-astakos-app/astakos/im/templates/im/auth/third_party_provider_generic_login.html b/snf-astakos-app/astakos/im/templates/im/auth/third_party_provider_generic_login.html
index 7a22656..edbb21b 100644
--- a/snf-astakos-app/astakos/im/templates/im/auth/third_party_provider_generic_login.html
+++ b/snf-astakos-app/astakos/im/templates/im/auth/third_party_provider_generic_login.html
@@ -1,4 +1,4 @@
 {% load astakos_tags %}
-<h2><a href="{% provider_login_url provider %}">
+<h2><a href="{% provider_login_url provider 1 %}">
     {{ provider.get_primary_login_prompt_display|safe }} {{ provider.get_title_display }}
 </a></h2>
diff --git a/snf-astakos-app/astakos/im/templates/im/auth/third_party_provider_generic_login_prompt.html b/snf-astakos-app/astakos/im/templates/im/auth/third_party_provider_generic_login_prompt.html
index a2222d6..3add187 100644
--- a/snf-astakos-app/astakos/im/templates/im/auth/third_party_provider_generic_login_prompt.html
+++ b/snf-astakos-app/astakos/im/templates/im/auth/third_party_provider_generic_login_prompt.html
@@ -1,4 +1,4 @@
 {% load astakos_tags %}
-
-<a href="{% provider_login_url provider %}" alt="{{ provider.get_title_display }}" class="icons"><img src="{{ provider.get_icon_url_display }}" alt="{{ provider.get_title_display }}" /></a>
+<a href="{% provider_login_url provider 1 %}" alt="{{ provider.get_title_display }}" class="icons">
+<img src="{{ provider.get_icon_url_display }}" alt="{{ provider.get_title_display }}" /></a>
 
diff --git a/snf-astakos-app/astakos/im/templatetags/astakos_tags.py b/snf-astakos-app/astakos/im/templatetags/astakos_tags.py
index ed6dac3..bb036ed 100644
--- a/snf-astakos-app/astakos/im/templatetags/astakos_tags.py
+++ b/snf-astakos-app/astakos/im/templatetags/astakos_tags.py
@@ -183,7 +183,7 @@ def get_grant_value(rname, form):
 
 @register.tag(name="provider_login_url")
 @basictag(takes_context=True)
-def provider_login_url(context, provider):
+def provider_login_url(context, provider, from_login=False):
     request = context['request'].REQUEST
     next = request.get('next', None)
     code = request.get('code', None)
@@ -196,6 +196,8 @@ def provider_login_url(context, provider):
         attrs['code'] = code
     if key:
         attrs['key'] = key
+    if from_login:
+        attrs['from_login'] = 1
 
     url = provider.add_url
 
