commit ed88de55d011ba10d0ac23d58b7312c317d5c888
Author: Kostas Papadimitriou <kpap@grnet.gr>
Date:   Tue Sep 24 17:16:29 2013 +0300

    astakos: Shibboleth EPPN migration functionality
    
    Prior to this commit astakos used the mod_shib2 EPPN header value as the
    unique identifier for associating shibboleth idp users to astakos user entries.
    
    This commit alters this behaviour and from now on astakos resloves unique
    identifier from the REMOTE_USER header. REMOTE_USER is a header mod_shib2 sets
    containing a value of the available shibboleth IdP metadata. The metadata
    key (persistent-id or eppn in most common scenarios) used can be configured
    from within shibboleth2.xml config file.
    
    <ApplicationDefaults id="default" .... .... REMOTE_USER="persistent-id"...>
    
    An additional setting ``ASTAKOS_SHIBBOLETH_MIGRATE_EPPN`` is added in order
    to facilitate migration of existing EPPN entries to persistent-id/targeted-id
    (or whichever metadata the REMOTE_USER maps to). When set to ``True``, after
    each shibboleth login astakos will try to migrate the existing EPPN entry
    by following the below mentioned steps:
    
    * If no REMOTE_USER header exists or is empty, redirect to an error view.
      Otherwise continue to the next step.
    * Resolve EPPN header and check if an account is currently associated with this
      EPPN.
    * If user exists, retrieve user's shibboleth entry (AstakosUserAuthProvider
      instance) and replace stored identifier (EPPN) with the identifier contained
      in REMOTE_USER header.
    * Continue to login or signup process using REMOTE_USER value as the unique
      user identifier that associates astakos user to the shibboleth account.
    
    Conflicts:
    
    	snf-astakos-app/astakos/im/messages.py
    	snf-astakos-app/astakos/im/tests/auth.py

diff --git a/snf-astakos-app/astakos/im/views/target/shibboleth.py b/snf-astakos-app/astakos/im/views/target/shibboleth.py
index 25a2f19..9048c02 100644
--- a/snf-astakos-app/astakos/im/views/target/shibboleth.py
+++ b/snf-astakos-app/astakos/im/views/target/shibboleth.py
@@ -38,7 +38,8 @@ from django.views.decorators.http import require_http_methods
 from django.http import HttpResponseRedirect
 
 from astakos.im.util import login_url
-from astakos.im.models import AstakosUser
+from astakos.im.models import AstakosUser, AstakosUserAuthProvider, \
+    PendingThirdPartyUser
 from astakos.im import settings
 from astakos.im.views.target import get_pending_key, \
     handle_third_party_signup, handle_third_party_login, \
@@ -51,6 +52,38 @@ import logging
 logger = logging.getLogger(__name__)
 
 
+def migrate_eppn_to_remote_id(eppn, remote_id):
+    """
+    Retrieve active and pending accounts that are associated with shibboleth
+    using EPPN as the third party unique identifier update them by storing
+    REMOTE_USER value instead.
+    """
+    if eppn == remote_id:
+        return
+
+    try:
+        provider = AstakosUserAuthProvider.objects.get(module='shibboleth',
+                                                       identifier=eppn)
+        msg = "Migrating user %r eppn (%s -> %s)"
+        logger.info(msg, provider.user.log_display, eppn, remote_id)
+        provider.identifier = remote_id
+        provider.save()
+    except AstakosUserAuthProvider.DoesNotExist:
+        pass
+
+    pending_users = \
+            PendingThirdPartyUser.objects.filter(third_party_identifier=eppn,
+                                                 provider='shibboleth')
+
+    for pending in pending_users:
+        msg = "Migrating pending user %s eppn (%s -> %s)"
+        logger.info(msg, pending.email, eppn, remote_id)
+        pending.third_party_identifier = remote_id
+        pending.save()
+
+    return remote_id
+
+
 class Tokens:
     # these are mapped by the Shibboleth SP software
     SHIB_EPPN = "HTTP_EPPN"  # eduPersonPrincipalName
@@ -90,15 +123,18 @@ def login(
     logger.info("shibboleth request: %r" % shibboleth_headers)
 
     try:
-        eppn = tokens.get(Tokens.SHIB_EPPN)
+        eppn = tokens.get(Tokens.SHIB_EPPN, None)
+        user_id = tokens.get(Tokens.SHIB_REMOTE_USER)
         fullname, first_name, last_name, email = None, None, None, None
         if global_settings.DEBUG and not eppn:
+            user_id = getattr(global_settings, 'SHIBBOLETH_TEST_REMOTE_USER',
+                              None)
             eppn = getattr(global_settings, 'SHIBBOLETH_TEST_EPPN', None)
             fullname = getattr(global_settings, 'SHIBBOLETH_TEST_FULLNAME',
                                None)
 
-        if not eppn:
-            raise KeyError(_(astakos_messages.SHIBBOLETH_MISSING_EPPN) % {
+        if not user_id:
+            raise KeyError(_(astakos_messages.SHIBBOLETH_MISSING_USER_ID) % {
                 'domain': settings.BASE_HOST,
                 'contact_email': settings.CONTACT_EMAIL
             })
@@ -126,17 +162,18 @@ def login(
         messages.error(request, e.message)
         return HttpResponseRedirect(login_url(request))
 
+    if settings.SHIBBOLETH_MIGRATE_EPPN:
+        migrate_eppn_to_remote_id(eppn, user_id)
+
     affiliation = tokens.get(Tokens.SHIB_EP_AFFILIATION, 'Shibboleth')
     email = tokens.get(Tokens.SHIB_MAIL, '')
-    eppn_info = tokens.get(Tokens.SHIB_EPPN)
-    provider_info = {'eppn': eppn_info, 'email': email, 'name': fullname,
-                     'headers': shibboleth_headers}
-    userid = eppn
+    provider_info = {'eppn': eppn, 'email': email, 'name': fullname,
+                     'headers': shibboleth_headers, 'user_id': user_id}
 
 
     try:
         return handle_third_party_login(request, 'shibboleth',
-                                        eppn, provider_info,
+                                        user_id, provider_info,
                                         affiliation, third_party_key)
     except AstakosUser.DoesNotExist, e:
         third_party_key = get_pending_key(request)
@@ -144,7 +181,7 @@ def login(
                      'first_name': first_name,
                      'last_name': last_name,
                      'email': email}
-        return handle_third_party_signup(request, userid, 'shibboleth',
+        return handle_third_party_signup(request, user_id, 'shibboleth',
                                          third_party_key,
                                          provider_info,
                                          user_info,
