Merge branch 'master' into newstyles
authorKostas Papadimitriou <kpap@grnet.gr>
Mon, 30 Jul 2012 15:34:22 +0000 (18:34 +0300)
committerKostas Papadimitriou <kpap@grnet.gr>
Mon, 30 Jul 2012 15:34:22 +0000 (18:34 +0300)
14 files changed:
snf-astakos-app/astakos/im/activation_backends.py
snf-astakos-app/astakos/im/api/admin.py
snf-astakos-app/astakos/im/management/commands/modifyuser.py
snf-astakos-app/astakos/im/migrations/0013_auto__del_unique_additionalmail_email.py [new file with mode: 0644]
snf-astakos-app/astakos/im/migrations/0014_auto__add_unique_astakosuser_third_party_identifier_provider.py [new file with mode: 0644]
snf-astakos-app/astakos/im/models.py
snf-astakos-app/astakos/im/target/oauth2/__init__.py [deleted file]
snf-astakos-app/astakos/im/target/oauth2/_version.py [deleted file]
snf-astakos-app/astakos/im/target/oauth2/clients/__init__.py [deleted file]
snf-astakos-app/astakos/im/target/oauth2/clients/imap.py [deleted file]
snf-astakos-app/astakos/im/target/oauth2/clients/smtp.py [deleted file]
snf-astakos-app/astakos/im/templates/im/admin_notification.txt
snf-astakos-app/astakos/im/templates/im/feedback_mail.txt
snf-astakos-app/astakos/im/views.py

index dc73ed1..71b0abe 100644 (file)
@@ -89,7 +89,7 @@ class ActivationBackend(object):
     
     def get_signup_form(self, provider='local', instance=None):
         """
-        Returns the form class name
+        Returns a form instance of the relevant class
         """
         main = provider.capitalize() if provider == 'local' else 'ThirdParty'
         suffix  = 'UserCreationForm'
@@ -152,7 +152,7 @@ class InvitationsBackend(ActivationBackend):
 
     def get_signup_form(self, provider='local', instance=None):
         """
-        Returns the form class
+        Returns a form instance of the relevant class
         
         raises Invitation.DoesNotExist and ValueError if invitation is consumed
         or invitation username is reserved.
index 81dbeb4..90c5063 100644 (file)
@@ -84,9 +84,6 @@ def api_method(http_method=None, token_required=False, perms=None):
                         raise Unauthorized('Access denied')
                     try:
                         user = AstakosUser.objects.get(auth_token=x_auth_token)
-                        ## Check if the token has expired.
-                        #if (time() - mktime(user.auth_token_expires.timetuple())) > 0:
-                        #    raise Unauthorized('Authentication expired')
                         if not user.has_perms(perms):
                             raise Forbidden('Unauthorized request')
                     except AstakosUser.DoesNotExist, e:
@@ -196,9 +193,13 @@ def get_menu(request, with_extra_links=False, with_signout=True):
     cookie = urllib.unquote(request.COOKIES.get(COOKIE_NAME, ''))
     email = cookie.partition('|')[0]
     try:
+        if not email:
+            raise ValueError
         user = AstakosUser.objects.get(email=email, is_active=True)
     except AstakosUser.DoesNotExist:
         pass
+    except ValueError:
+        pass
     else:
         l = []
         l.append({ 'url': absolute(reverse('astakos.im.views.index')),
index 45a5a8b..9138524 100644 (file)
@@ -58,11 +58,20 @@ class Command(BaseCommand):
             dest='password',
             metavar='PASSWORD',
             help="Set user's password"),
+        make_option('--provider',
+            dest='provider',
+            metavar='PROVIDER',
+            help="Set user's provider"),
         make_option('--renew-token',
             action='store_true',
             dest='renew_token',
             default=False,
             help="Renew the user's token"),
+        make_option('--renew-password',
+            action='store_true',
+            dest='renew_password',
+            default=False,
+            help="Renew the user's password"),
         make_option('--set-admin',
             action='store_true',
             dest='admin',
@@ -173,6 +182,16 @@ class Command(BaseCommand):
         if password is not None:
             user.set_password(password)
         
+        provider = options.get('provider')
+        if provider is not None:
+            user.provider = provider
+        
+        
+        password = None
+        if options['renew_password']:
+            password = AstakosUser.objects.make_random_password()
+            user.set_password(password)
+        
         if options['renew_token']:
             user.renew_token()
         
@@ -180,3 +199,6 @@ class Command(BaseCommand):
             user.save()
         except ValidationError, e:
             raise CommandError(e)
+        
+        if password:
+            self.stdout.write('User\'s new password: %s\n' % password)
diff --git a/snf-astakos-app/astakos/im/migrations/0013_auto__del_unique_additionalmail_email.py b/snf-astakos-app/astakos/im/migrations/0013_auto__del_unique_additionalmail_email.py
new file mode 100644 (file)
index 0000000..99c3eca
--- /dev/null
@@ -0,0 +1,120 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        
+        # Removing unique constraint on 'AdditionalMail', fields ['email']
+        db.delete_unique('im_additionalmail', ['email'])
+
+
+    def backwards(self, orm):
+        
+        # Adding unique constraint on 'AdditionalMail', fields ['email']
+        db.create_unique('im_additionalmail', ['email'])
+
+
+    models = {
+        'auth.group': {
+            'Meta': {'object_name': 'Group'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        'auth.permission': {
+            'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        'auth.user': {
+            'Meta': {'object_name': 'User'},
+            'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        'contenttypes.contenttype': {
+            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        'im.additionalmail': {
+            'Meta': {'object_name': 'AdditionalMail'},
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosUser']"})
+        },
+        'im.approvalterms': {
+            'Meta': {'object_name': 'ApprovalTerms'},
+            'date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 7, 9, 10, 21, 10, 951071)', 'db_index': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'location': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'im.astakosuser': {
+            'Meta': {'object_name': 'AstakosUser', '_ormbases': ['auth.User']},
+            'activation_sent': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'affiliation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'auth_token': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
+            'auth_token_created': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+            'auth_token_expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+            'date_signed_terms': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'email_verified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'has_credits': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'has_signed_terms': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'invitations': ('django.db.models.fields.IntegerField', [], {'default': '100'}),
+            'is_verified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'level': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'provider': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'third_party_identifier': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {}),
+            'user_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'primary_key': 'True'})
+        },
+        'im.emailchange': {
+            'Meta': {'object_name': 'EmailChange'},
+            'activation_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40', 'db_index': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'new_email_address': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+            'requested_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 7, 9, 10, 21, 10, 952702)'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'emailchange_user'", 'unique': 'True', 'to': "orm['im.AstakosUser']"})
+        },
+        'im.invitation': {
+            'Meta': {'object_name': 'Invitation'},
+            'code': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+            'consumed': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'inviter': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'invitations_sent'", 'null': 'True', 'to': "orm['im.AstakosUser']"}),
+            'is_consumed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'realname': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'})
+        },
+        'im.service': {
+            'Meta': {'object_name': 'Service'},
+            'auth_token': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
+            'auth_token_created': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+            'auth_token_expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+            'icon': ('django.db.models.fields.FilePathField', [], {'max_length': '100', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+            'url': ('django.db.models.fields.FilePathField', [], {'max_length': '100'})
+        }
+    }
+
+    complete_apps = ['im']
diff --git a/snf-astakos-app/astakos/im/migrations/0014_auto__add_unique_astakosuser_third_party_identifier_provider.py b/snf-astakos-app/astakos/im/migrations/0014_auto__add_unique_astakosuser_third_party_identifier_provider.py
new file mode 100644 (file)
index 0000000..820a7cc
--- /dev/null
@@ -0,0 +1,120 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        
+        # Adding unique constraint on 'AstakosUser', fields ['third_party_identifier', 'provider']
+        db.create_unique('im_astakosuser', ['third_party_identifier', 'provider'])
+
+
+    def backwards(self, orm):
+        
+        # Removing unique constraint on 'AstakosUser', fields ['third_party_identifier', 'provider']
+        db.delete_unique('im_astakosuser', ['third_party_identifier', 'provider'])
+
+
+    models = {
+        'auth.group': {
+            'Meta': {'object_name': 'Group'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        'auth.permission': {
+            'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        'auth.user': {
+            'Meta': {'object_name': 'User'},
+            'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        'contenttypes.contenttype': {
+            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        'im.additionalmail': {
+            'Meta': {'object_name': 'AdditionalMail'},
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosUser']"})
+        },
+        'im.approvalterms': {
+            'Meta': {'object_name': 'ApprovalTerms'},
+            'date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 7, 9, 10, 24, 0, 566720)', 'db_index': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'location': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'im.astakosuser': {
+            'Meta': {'unique_together': "(('provider', 'third_party_identifier'),)", 'object_name': 'AstakosUser', '_ormbases': ['auth.User']},
+            'activation_sent': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'affiliation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'auth_token': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
+            'auth_token_created': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+            'auth_token_expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+            'date_signed_terms': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'email_verified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'has_credits': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'has_signed_terms': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'invitations': ('django.db.models.fields.IntegerField', [], {'default': '100'}),
+            'is_verified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'level': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'provider': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'third_party_identifier': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {}),
+            'user_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'primary_key': 'True'})
+        },
+        'im.emailchange': {
+            'Meta': {'object_name': 'EmailChange'},
+            'activation_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40', 'db_index': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'new_email_address': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+            'requested_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 7, 9, 10, 24, 0, 568837)'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'emailchange_user'", 'unique': 'True', 'to': "orm['im.AstakosUser']"})
+        },
+        'im.invitation': {
+            'Meta': {'object_name': 'Invitation'},
+            'code': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+            'consumed': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'inviter': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'invitations_sent'", 'null': 'True', 'to': "orm['im.AstakosUser']"}),
+            'is_consumed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'realname': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'})
+        },
+        'im.service': {
+            'Meta': {'object_name': 'Service'},
+            'auth_token': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
+            'auth_token_created': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+            'auth_token_expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+            'icon': ('django.db.models.fields.FilePathField', [], {'max_length': '100', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+            'url': ('django.db.models.fields.FilePathField', [], {'max_length': '100'})
+        }
+    }
+
+    complete_apps = ['im']
index c77c198..b2564af 100644 (file)
@@ -96,6 +96,9 @@ class AstakosUser(User):
     __has_signed_terms = False
     __groupnames = []
     
+    class Meta:
+        unique_together = ("provider", "third_party_identifier")
+    
     def __init__(self, *args, **kwargs):
         super(AstakosUser, self).__init__(*args, **kwargs)
         self.__has_signed_terms = self.has_signed_terms
@@ -369,7 +372,7 @@ class AdditionalMail(models.Model):
     Model for registring invitations
     """
     owner = models.ForeignKey(AstakosUser)
-    email = models.EmailField(unique=True)
+    email = models.EmailField()
 
 def create_astakos_user(u):
     try:
diff --git a/snf-astakos-app/astakos/im/target/oauth2/__init__.py b/snf-astakos-app/astakos/im/target/oauth2/__init__.py
deleted file mode 100644 (file)
index 835270e..0000000
+++ /dev/null
@@ -1,860 +0,0 @@
-"""
-The MIT License
-
-Copyright (c) 2007-2010 Leah Culver, Joe Stump, Mark Paschal, Vic Fryzel
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
-"""
-
-import base64
-import urllib
-import time
-import random
-import urlparse
-import hmac
-import binascii
-import httplib2
-
-try:
-    from urlparse import parse_qs
-    parse_qs # placate pyflakes
-except ImportError:
-    # fall back for Python 2.5
-    from cgi import parse_qs
-
-try:
-    from hashlib import sha1
-    sha = sha1
-except ImportError:
-    # hashlib was added in Python 2.5
-    import sha
-
-import _version
-
-__version__ = _version.__version__
-
-OAUTH_VERSION = '1.0'  # Hi Blaine!
-HTTP_METHOD = 'GET'
-SIGNATURE_METHOD = 'PLAINTEXT'
-
-
-class Error(RuntimeError):
-    """Generic exception class."""
-
-    def __init__(self, message='OAuth error occurred.'):
-        self._message = message
-
-    @property
-    def message(self):
-        """A hack to get around the deprecation errors in 2.6."""
-        return self._message
-
-    def __str__(self):
-        return self._message
-
-
-class MissingSignature(Error):
-    pass
-
-
-def build_authenticate_header(realm=''):
-    """Optional WWW-Authenticate header (401 error)"""
-    return {'WWW-Authenticate': 'OAuth realm="%s"' % realm}
-
-
-def build_xoauth_string(url, consumer, token=None):
-    """Build an XOAUTH string for use in SMTP/IMPA authentication."""
-    request = Request.from_consumer_and_token(consumer, token,
-        "GET", url)
-
-    signing_method = SignatureMethod_HMAC_SHA1()
-    request.sign_request(signing_method, consumer, token)
-
-    params = []
-    for k, v in sorted(request.iteritems()):
-        if v is not None:
-            params.append('%s="%s"' % (k, escape(v)))
-
-    return "%s %s %s" % ("GET", url, ','.join(params))
-
-
-def to_unicode(s):
-    """ Convert to unicode, raise exception with instructive error
-    message if s is not unicode, ascii, or utf-8. """
-    if not isinstance(s, unicode):
-        if not isinstance(s, str):
-            raise TypeError('You are required to pass either unicode or string here, not: %r (%s)' % (type(s), s))
-        try:
-            s = s.decode('utf-8')
-        except UnicodeDecodeError, le:
-            raise TypeError('You are required to pass either a unicode object or a utf-8 string here. You passed a Python string object which contained non-utf-8: %r. The UnicodeDecodeError that resulted from attempting to interpret it as utf-8 was: %s' % (s, le,))
-    return s
-
-def to_utf8(s):
-    return to_unicode(s).encode('utf-8')
-
-def to_unicode_if_string(s):
-    if isinstance(s, basestring):
-        return to_unicode(s)
-    else:
-        return s
-
-def to_utf8_if_string(s):
-    if isinstance(s, basestring):
-        return to_utf8(s)
-    else:
-        return s
-
-def to_unicode_optional_iterator(x):
-    """
-    Raise TypeError if x is a str containing non-utf8 bytes or if x is
-    an iterable which contains such a str.
-    """
-    if isinstance(x, basestring):
-        return to_unicode(x)
-
-    try:
-        l = list(x)
-    except TypeError, e:
-        assert 'is not iterable' in str(e)
-        return x
-    else:
-        return [ to_unicode(e) for e in l ]
-
-def to_utf8_optional_iterator(x):
-    """
-    Raise TypeError if x is a str or if x is an iterable which
-    contains a str.
-    """
-    if isinstance(x, basestring):
-        return to_utf8(x)
-
-    try:
-        l = list(x)
-    except TypeError, e:
-        assert 'is not iterable' in str(e)
-        return x
-    else:
-        return [ to_utf8_if_string(e) for e in l ]
-
-def escape(s):
-    """Escape a URL including any /."""
-    return urllib.quote(s.encode('utf-8'), safe='~')
-
-def generate_timestamp():
-    """Get seconds since epoch (UTC)."""
-    return int(time.time())
-
-
-def generate_nonce(length=8):
-    """Generate pseudorandom number."""
-    return ''.join([str(random.randint(0, 9)) for i in range(length)])
-
-
-def generate_verifier(length=8):
-    """Generate pseudorandom number."""
-    return ''.join([str(random.randint(0, 9)) for i in range(length)])
-
-
-class Consumer(object):
-    """A consumer of OAuth-protected services.
-    The OAuth consumer is a "third-party" service that wants to access
-    protected resources from an OAuth service provider on behalf of an end
-    user. It's kind of the OAuth client.
-    Usually a consumer must be registered with the service provider by the
-    developer of the consumer software. As part of that process, the service
-    provider gives the consumer a *key* and a *secret* with which the consumer
-    software can identify itself to the service. The consumer will include its
-    key in each request to identify itself, but will use its secret only when
-    signing requests, to prove that the request is from that particular
-    registered consumer.
-    Once registered, the consumer can then use its consumer credentials to ask
-    the service provider for a request token, kicking off the OAuth
-    authorization process.
-    """
-
-    key = None
-    secret = None
-
-    def __init__(self, key, secret):
-        self.key = key
-        self.secret = secret
-
-        if self.key is None or self.secret is None:
-            raise ValueError("Key and secret must be set.")
-
-    def __str__(self):
-        data = {'oauth_consumer_key': self.key,
-            'oauth_consumer_secret': self.secret}
-
-        return urllib.urlencode(data)
-
-
-class Token(object):
-    """An OAuth credential used to request authorization or a protected
-    resource.
-    Tokens in OAuth comprise a *key* and a *secret*. The key is included in
-    requests to identify the token being used, but the secret is used only in
-    the signature, to prove that the requester is who the server gave the
-    token to.
-    When first negotiating the authorization, the consumer asks for a *request
-    token* that the live user authorizes with the service provider. The
-    consumer then exchanges the request token for an *access token* that can
-    be used to access protected resources.
-    """
-
-    key = None
-    secret = None
-    callback = None
-    callback_confirmed = None
-    verifier = None
-
-    def __init__(self, key, secret):
-        self.key = key
-        self.secret = secret
-
-        if self.key is None or self.secret is None:
-            raise ValueError("Key and secret must be set.")
-
-    def set_callback(self, callback):
-        self.callback = callback
-        self.callback_confirmed = 'true'
-
-    def set_verifier(self, verifier=None):
-        if verifier is not None:
-            self.verifier = verifier
-        else:
-            self.verifier = generate_verifier()
-
-    def get_callback_url(self):
-        if self.callback and self.verifier:
-            # Append the oauth_verifier.
-            parts = urlparse.urlparse(self.callback)
-            scheme, netloc, path, params, query, fragment = parts[:6]
-            if query:
-                query = '%s&oauth_verifier=%s' % (query, self.verifier)
-            else:
-                query = 'oauth_verifier=%s' % self.verifier
-            return urlparse.urlunparse((scheme, netloc, path, params,
-                query, fragment))
-        return self.callback
-
-    def to_string(self):
-        """Returns this token as a plain string, suitable for storage.
-        The resulting string includes the token's secret, so you should never
-        send or store this string where a third party can read it.
-        """
-
-        data = {
-            'oauth_token': self.key,
-            'oauth_token_secret': self.secret,
-        }
-
-        if self.callback_confirmed is not None:
-            data['oauth_callback_confirmed'] = self.callback_confirmed
-        return urllib.urlencode(data)
-    @staticmethod
-    def from_string(s):
-        """Deserializes a token from a string like one returned by
-        `to_string()`."""
-
-        if not len(s):
-            raise ValueError("Invalid parameter string.")
-
-        params = parse_qs(s, keep_blank_values=False)
-        if not len(params):
-            raise ValueError("Invalid parameter string.")
-
-        try:
-            key = params['oauth_token'][0]
-        except Exception:
-            raise ValueError("'oauth_token' not found in OAuth request.")
-
-        try:
-            secret = params['oauth_token_secret'][0]
-        except Exception:
-            raise ValueError("'oauth_token_secret' not found in " 
-                "OAuth request.")
-
-        token = Token(key, secret)
-        try:
-            token.callback_confirmed = params['oauth_callback_confirmed'][0]
-        except KeyError:
-            pass  # 1.0, no callback confirmed.
-        return token
-
-    def __str__(self):
-        return self.to_string()
-
-
-def setter(attr):
-    name = attr.__name__
-    def getter(self):
-        try:
-            return self.__dict__[name]
-        except KeyError:
-            raise AttributeError(name)
-    def deleter(self):
-        del self.__dict__[name]
-    return property(getter, attr, deleter)
-
-
-class Request(dict):
-    """The parameters and information for an HTTP request, suitable for
-    authorizing with OAuth credentials.
-    When a consumer wants to access a service's protected resources, it does
-    so using a signed HTTP request identifying itself (the consumer) with its
-    key, and providing an access token authorized by the end user to access
-    those resources.
-    """
-    version = OAUTH_VERSION
-
-    def __init__(self, method=HTTP_METHOD, url=None, parameters=None,
-                 body='', is_form_encoded=False):
-        if url is not None:
-            self.url = to_unicode(url)
-        self.method = method
-        if parameters is not None:
-            for k, v in parameters.iteritems():
-                k = to_unicode(k)
-                v = to_unicode_optional_iterator(v)
-                self[k] = v
-        self.body = body
-        self.is_form_encoded = is_form_encoded
-
-
-    @setter
-    def url(self, value):
-        self.__dict__['url'] = value
-        if value is not None:
-            scheme, netloc, path, params, query, fragment = urlparse.urlparse(value)
-
-            # Exclude default port numbers.
-            if scheme == 'http' and netloc[-3:] == ':80':
-                netloc = netloc[:-3]
-            elif scheme == 'https' and netloc[-4:] == ':443':
-                netloc = netloc[:-4]
-            if scheme not in ('http', 'https'):
-                raise ValueError("Unsupported URL %s (%s)." % (value, scheme))
-
-            # Normalized URL excludes params, query, and fragment.
-            self.normalized_url = urlparse.urlunparse((scheme, netloc, path, None, None, None))
-        else:
-            self.normalized_url = None
-            self.__dict__['url'] = None
-    @setter
-    def method(self, value):
-        self.__dict__['method'] = value.upper()
-    def _get_timestamp_nonce(self):
-        return self['oauth_timestamp'], self['oauth_nonce']
-    def get_nonoauth_parameters(self):
-        """Get any non-OAuth parameters."""
-        return dict([(k, v) for k, v in self.iteritems() 
-                    if not k.startswith('oauth_')])
-    def to_header(self, realm=''):
-        """Serialize as a header for an HTTPAuth request."""
-        oauth_params = ((k, v) for k, v in self.items() 
-                            if k.startswith('oauth_'))
-        stringy_params = ((k, escape(str(v))) for k, v in oauth_params)
-        header_params = ('%s="%s"' % (k, v) for k, v in stringy_params)
-        params_header = ', '.join(header_params)
-        auth_header = 'OAuth realm="%s"' % realm
-        if params_header:
-            auth_header = "%s, %s" % (auth_header, params_header)
-        return {'Authorization': auth_header}
-    def to_postdata(self):
-        """Serialize as post data for a POST request."""
-        d = {}
-        for k, v in self.iteritems():
-            d[k.encode('utf-8')] = to_utf8_optional_iterator(v)
-
-        # tell urlencode to deal with sequence values and map them correctly
-        # to resulting querystring. for example self["k"] = ["v1", "v2"] will
-        # result in 'k=v1&k=v2' and not k=%5B%27v1%27%2C+%27v2%27%5D
-        return urllib.urlencode(d, True).replace('+', '%20')
-    def to_url(self):
-        """Serialize as a URL for a GET request."""
-        base_url = urlparse.urlparse(self.url)
-        try:
-            query = base_url.query
-        except AttributeError:
-            # must be python <2.5
-            query = base_url[4]
-        query = parse_qs(query)
-        for k, v in self.items():
-            query.setdefault(k, []).append(v)
-        
-        try:
-            scheme = base_url.scheme
-            netloc = base_url.netloc
-            path = base_url.path
-            params = base_url.params
-            fragment = base_url.fragment
-        except AttributeError:
-            # must be python <2.5
-            scheme = base_url[0]
-            netloc = base_url[1]
-            path = base_url[2]
-            params = base_url[3]
-            fragment = base_url[5]
-        
-        url = (scheme, netloc, path, params,
-               urllib.urlencode(query, True), fragment)
-        return urlparse.urlunparse(url)
-
-    def get_parameter(self, parameter):
-        ret = self.get(parameter)
-        if ret is None:
-            raise Error('Parameter not found: %s' % parameter)
-
-        return ret
-
-    def get_normalized_parameters(self):
-        """Return a string that contains the parameters that must be signed."""
-        items = []
-        for key, value in self.iteritems():
-            if key == 'oauth_signature':
-                continue
-            # 1.0a/9.1.1 states that kvp must be sorted by key, then by value,
-            # so we unpack sequence values into multiple items for sorting.
-            if isinstance(value, basestring):
-                items.append((to_utf8_if_string(key), to_utf8(value)))
-            else:
-                try:
-                    value = list(value)
-                except TypeError, e:
-                    assert 'is not iterable' in str(e)
-                    items.append((to_utf8_if_string(key), to_utf8_if_string(value)))
-                else:
-                    items.extend((to_utf8_if_string(key), to_utf8_if_string(item)) for item in value)
-
-        # Include any query string parameters from the provided URL
-        query = urlparse.urlparse(self.url)[4]
-
-        url_items = self._split_url_string(query).items()
-        url_items = [(to_utf8(k), to_utf8(v)) for k, v in url_items if k != 'oauth_signature' ]
-        items.extend(url_items)
-
-        items.sort()
-        encoded_str = urllib.urlencode(items)
-        # Encode signature parameters per Oauth Core 1.0 protocol
-        # spec draft 7, section 3.6
-        # (http://tools.ietf.org/html/draft-hammer-oauth-07#section-3.6)
-        # Spaces must be encoded with "%20" instead of "+"
-        return encoded_str.replace('+', '%20').replace('%7E', '~')
-
-    def sign_request(self, signature_method, consumer, token):
-        """Set the signature parameter to the result of sign."""
-
-        if not self.is_form_encoded:
-            # according to
-            # http://oauth.googlecode.com/svn/spec/ext/body_hash/1.0/oauth-bodyhash.html
-            # section 4.1.1 "OAuth Consumers MUST NOT include an
-            # oauth_body_hash parameter on requests with form-encoded
-            # request bodies."
-            self['oauth_body_hash'] = base64.b64encode(sha(self.body).digest())
-
-        if 'oauth_consumer_key' not in self:
-            self['oauth_consumer_key'] = consumer.key
-
-        if token and 'oauth_token' not in self:
-            self['oauth_token'] = token.key
-
-        self['oauth_signature_method'] = signature_method.name
-        self['oauth_signature'] = signature_method.sign(self, consumer, token)
-    @classmethod
-    def make_timestamp(cls):
-        """Get seconds since epoch (UTC)."""
-        return str(int(time.time()))
-    @classmethod
-    def make_nonce(cls):
-        """Generate pseudorandom number."""
-        return str(random.randint(0, 100000000))
-    @classmethod
-    def from_request(cls, http_method, http_url, headers=None, parameters=None,
-            query_string=None):
-        """Combines multiple parameter sources."""
-        if parameters is None:
-            parameters = {}
-        # Headers
-        if headers and 'Authorization' in headers:
-            auth_header = headers['Authorization']
-            # Check that the authorization header is OAuth.
-            if auth_header[:6] == 'OAuth ':
-                auth_header = auth_header[6:]
-                try:
-                    # Get the parameters from the header.
-                    header_params = cls._split_header(auth_header)
-                    parameters.update(header_params)
-                except:
-                    raise Error('Unable to parse OAuth parameters from '
-                        'Authorization header.')
-        # GET or POST query string.
-        if query_string:
-            query_params = cls._split_url_string(query_string)
-            parameters.update(query_params)
-        # URL parameters.
-        param_str = urlparse.urlparse(http_url)[4] # query
-        url_params = cls._split_url_string(param_str)
-        parameters.update(url_params)
-        if parameters:
-            return cls(http_method, http_url, parameters)
-        return None
-    @classmethod
-    def from_consumer_and_token(cls, consumer, token=None,
-            http_method=HTTP_METHOD, http_url=None, parameters=None,
-            body='', is_form_encoded=False):
-        if not parameters:
-            parameters = {}
-        defaults = {
-            'oauth_consumer_key': consumer.key,
-            'oauth_timestamp': cls.make_timestamp(),
-            'oauth_nonce': cls.make_nonce(),
-            'oauth_version': cls.version,
-        }
-        defaults.update(parameters)
-        parameters = defaults
-        if token:
-            parameters['oauth_token'] = token.key
-            if token.verifier:
-                parameters['oauth_verifier'] = token.verifier
-        return Request(http_method, http_url, parameters, body=body, 
-                       is_form_encoded=is_form_encoded)
-    @classmethod
-    def from_token_and_callback(cls, token, callback=None, 
-        http_method=HTTP_METHOD, http_url=None, parameters=None):
-
-        if not parameters:
-            parameters = {}
-        parameters['oauth_token'] = token.key
-        if callback:
-            parameters['oauth_callback'] = callback
-        return cls(http_method, http_url, parameters)
-    @staticmethod
-    def _split_header(header):
-        """Turn Authorization: header into parameters."""
-        params = {}
-        parts = header.split(',')
-        for param in parts:
-            # Ignore realm parameter.
-            if param.find('realm') > -1:
-                continue
-            # Remove whitespace.
-            param = param.strip()
-            # Split key-value.
-            param_parts = param.split('=', 1)
-            # Remove quotes and unescape the value.
-            params[param_parts[0]] = urllib.unquote(param_parts[1].strip('\"'))
-        return params
-    @staticmethod
-    def _split_url_string(param_str):
-        """Turn URL string into parameters."""
-        parameters = parse_qs(param_str.encode('utf-8'), keep_blank_values=True)
-        for k, v in parameters.iteritems():
-            parameters[k] = urllib.unquote(v[0])
-        return parameters
-
-
-class Client(httplib2.Http):
-    """OAuthClient is a worker to attempt to execute a request."""
-
-    def __init__(self, consumer, token=None, cache=None, timeout=None,
-        proxy_info=None):
-
-        if consumer is not None and not isinstance(consumer, Consumer):
-            raise ValueError("Invalid consumer.")
-
-        if token is not None and not isinstance(token, Token):
-            raise ValueError("Invalid token.")
-
-        self.consumer = consumer
-        self.token = token
-        self.method = SignatureMethod_HMAC_SHA1()
-
-        httplib2.Http.__init__(self, cache=cache, timeout=timeout, proxy_info=proxy_info)
-
-    def set_signature_method(self, method):
-        if not isinstance(method, SignatureMethod):
-            raise ValueError("Invalid signature method.")
-
-        self.method = method
-
-    def request(self, uri, method="GET", body='', headers=None, 
-        redirections=httplib2.DEFAULT_MAX_REDIRECTS, connection_type=None):
-        DEFAULT_POST_CONTENT_TYPE = 'application/x-www-form-urlencoded'
-
-        if not isinstance(headers, dict):
-            headers = {}
-
-        if method == "POST":
-            headers['Content-Type'] = headers.get('Content-Type', 
-                DEFAULT_POST_CONTENT_TYPE)
-
-        is_form_encoded = \
-            headers.get('Content-Type') == 'application/x-www-form-urlencoded'
-
-        if is_form_encoded and body:
-            parameters = parse_qs(body)
-        else:
-            parameters = None
-
-        req = Request.from_consumer_and_token(self.consumer, 
-            token=self.token, http_method=method, http_url=uri, 
-            parameters=parameters, body=body, is_form_encoded=is_form_encoded)
-
-        req.sign_request(self.method, self.consumer, self.token)
-
-        schema, rest = urllib.splittype(uri)
-        if rest.startswith('//'):
-            hierpart = '//'
-        else:
-            hierpart = ''
-        host, rest = urllib.splithost(rest)
-
-        realm = schema + ':' + hierpart + host
-
-        if is_form_encoded:
-            body = req.to_postdata()
-        elif method == "GET":
-            uri = req.to_url()
-        else:
-            headers.update(req.to_header(realm=realm))
-
-        return httplib2.Http.request(self, uri, method=method, body=body,
-            headers=headers, redirections=redirections,
-            connection_type=connection_type)
-
-
-class Server(object):
-    """A skeletal implementation of a service provider, providing protected
-    resources to requests from authorized consumers.
-    This class implements the logic to check requests for authorization. You
-    can use it with your web server or web framework to protect certain
-    resources with OAuth.
-    """
-
-    timestamp_threshold = 300 # In seconds, five minutes.
-    version = OAUTH_VERSION
-    signature_methods = None
-
-    def __init__(self, signature_methods=None):
-        self.signature_methods = signature_methods or {}
-
-    def add_signature_method(self, signature_method):
-        self.signature_methods[signature_method.name] = signature_method
-        return self.signature_methods
-
-    def verify_request(self, request, consumer, token):
-        """Verifies an api call and checks all the parameters."""
-
-        self._check_version(request)
-        self._check_signature(request, consumer, token)
-        parameters = request.get_nonoauth_parameters()
-        return parameters
-
-    def build_authenticate_header(self, realm=''):
-        """Optional support for the authenticate header."""
-        return {'WWW-Authenticate': 'OAuth realm="%s"' % realm}
-
-    def _check_version(self, request):
-        """Verify the correct version of the request for this server."""
-        version = self._get_version(request)
-        if version and version != self.version:
-            raise Error('OAuth version %s not supported.' % str(version))
-
-    def _get_version(self, request):
-        """Return the version of the request for this server."""
-        try:
-            version = request.get_parameter('oauth_version')
-        except:
-            version = OAUTH_VERSION
-
-        return version
-
-    def _get_signature_method(self, request):
-        """Figure out the signature with some defaults."""
-        try:
-            signature_method = request.get_parameter('oauth_signature_method')
-        except:
-            signature_method = SIGNATURE_METHOD
-
-        try:
-            # Get the signature method object.
-            signature_method = self.signature_methods[signature_method]
-        except:
-            signature_method_names = ', '.join(self.signature_methods.keys())
-            raise Error('Signature method %s not supported try one of the following: %s' % (signature_method, signature_method_names))
-
-        return signature_method
-
-    def _get_verifier(self, request):
-        return request.get_parameter('oauth_verifier')
-
-    def _check_signature(self, request, consumer, token):
-        timestamp, nonce = request._get_timestamp_nonce()
-        self._check_timestamp(timestamp)
-        signature_method = self._get_signature_method(request)
-
-        try:
-            signature = request.get_parameter('oauth_signature')
-        except:
-            raise MissingSignature('Missing oauth_signature.')
-
-        # Validate the signature.
-        valid = signature_method.check(request, consumer, token, signature)
-
-        if not valid:
-            key, base = signature_method.signing_base(request, consumer, token)
-
-            raise Error('Invalid signature. Expected signature base ' 
-                'string: %s' % base)
-
-    def _check_timestamp(self, timestamp):
-        """Verify that timestamp is recentish."""
-        timestamp = int(timestamp)
-        now = int(time.time())
-        lapsed = now - timestamp
-        if lapsed > self.timestamp_threshold:
-            raise Error('Expired timestamp: given %d and now %s has a '
-                'greater difference than threshold %d' % (timestamp, now, 
-                    self.timestamp_threshold))
-
-
-class SignatureMethod(object):
-    """A way of signing requests.
-    The OAuth protocol lets consumers and service providers pick a way to sign
-    requests. This interface shows the methods expected by the other `oauth`
-    modules for signing requests. Subclass it and implement its methods to
-    provide a new way to sign requests.
-    """
-
-    def signing_base(self, request, consumer, token):
-        """Calculates the string that needs to be signed.
-
-        This method returns a 2-tuple containing the starting key for the
-        signing and the message to be signed. The latter may be used in error
-        messages to help clients debug their software.
-
-        """
-        raise NotImplementedError
-
-    def sign(self, request, consumer, token):
-        """Returns the signature for the given request, based on the consumer
-        and token also provided.
-
-        You should use your implementation of `signing_base()` to build the
-        message to sign. Otherwise it may be less useful for debugging.
-
-        """
-        raise NotImplementedError
-
-    def check(self, request, consumer, token, signature):
-        """Returns whether the given signature is the correct signature for
-        the given consumer and token signing the given request."""
-        built = self.sign(request, consumer, token)
-        return built == signature
-
-
-class SignatureMethod_HMAC_SHA1(SignatureMethod):
-    name = 'HMAC-SHA1'
-
-    def signing_base(self, request, consumer, token):
-        if not hasattr(request, 'normalized_url') or request.normalized_url is None:
-            raise ValueError("Base URL for request is not set.")
-
-        sig = (
-            escape(request.method),
-            escape(request.normalized_url),
-            escape(request.get_normalized_parameters()),
-        )
-
-        key = '%s&' % escape(consumer.secret)
-        if token:
-            key += escape(token.secret)
-        raw = '&'.join(sig)
-        return key, raw
-
-    def sign(self, request, consumer, token):
-        """Builds the base signature string."""
-        key, raw = self.signing_base(request, consumer, token)
-
-        hashed = hmac.new(key, raw, sha)
-
-        # Calculate the digest base 64.
-        return binascii.b2a_base64(hashed.digest())[:-1]
-
-
-class SignatureMethod_PLAINTEXT(SignatureMethod):
-
-    name = 'PLAINTEXT'
-
-    def signing_base(self, request, consumer, token):
-        """Concatenates the consumer key and secret with the token's
-        secret."""
-        sig = '%s&' % escape(consumer.secret)
-        if token:
-            sig = sig + escape(token.secret)
-        return sig, sig
-
-    def sign(self, request, consumer, token):
-        key, raw = self.signing_base(request, consumer, token)
-        return raw
diff --git a/snf-astakos-app/astakos/im/target/oauth2/_version.py b/snf-astakos-app/astakos/im/target/oauth2/_version.py
deleted file mode 100644 (file)
index 0d74e05..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-# This is the version of this source code.
-
-manual_verstr = "1.5"
-
-
-
-auto_build_num = "170"
-
-
-
-verstr = manual_verstr + "." + auto_build_num
-try:
-    from pyutil.version_class import Version as pyutil_Version
-    __version__ = pyutil_Version(verstr)
-except (ImportError, ValueError):
-    # Maybe there is no pyutil installed.
-    from distutils.version import LooseVersion as distutils_Version
-    __version__ = distutils_Version(verstr)
diff --git a/snf-astakos-app/astakos/im/target/oauth2/clients/__init__.py b/snf-astakos-app/astakos/im/target/oauth2/clients/__init__.py
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/snf-astakos-app/astakos/im/target/oauth2/clients/imap.py b/snf-astakos-app/astakos/im/target/oauth2/clients/imap.py
deleted file mode 100644 (file)
index 68b7cd8..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-"""
-The MIT License
-
-Copyright (c) 2007-2010 Leah Culver, Joe Stump, Mark Paschal, Vic Fryzel
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
-"""
-
-import oauth2
-import imaplib
-
-
-class IMAP4_SSL(imaplib.IMAP4_SSL):
-    """IMAP wrapper for imaplib.IMAP4_SSL that implements XOAUTH."""
-
-    def authenticate(self, url, consumer, token):
-        if consumer is not None and not isinstance(consumer, oauth2.Consumer):
-            raise ValueError("Invalid consumer.")
-
-        if token is not None and not isinstance(token, oauth2.Token):
-            raise ValueError("Invalid token.")
-
-        imaplib.IMAP4_SSL.authenticate(self, 'XOAUTH',
-            lambda x: oauth2.build_xoauth_string(url, consumer, token))
diff --git a/snf-astakos-app/astakos/im/target/oauth2/clients/smtp.py b/snf-astakos-app/astakos/im/target/oauth2/clients/smtp.py
deleted file mode 100644 (file)
index 3e7bf0b..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-"""
-The MIT License
-
-Copyright (c) 2007-2010 Leah Culver, Joe Stump, Mark Paschal, Vic Fryzel
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
-"""
-
-import oauth2
-import smtplib
-import base64
-
-
-class SMTP(smtplib.SMTP):
-    """SMTP wrapper for smtplib.SMTP that implements XOAUTH."""
-
-    def authenticate(self, url, consumer, token):
-        if consumer is not None and not isinstance(consumer, oauth2.Consumer):
-            raise ValueError("Invalid consumer.")
-
-        if token is not None and not isinstance(token, oauth2.Token):
-            raise ValueError("Invalid token.")
-
-        self.docmd('AUTH', 'XOAUTH %s' % \
-            base64.b64encode(oauth2.build_xoauth_string(url, consumer, token)))
index 40c365d..ec3c138 100644 (file)
@@ -21,4 +21,4 @@ Is active:      {{user.is_active}}
 Level:          {{user.level}}
 Invitations:    {{user.invitations}}
 
-Fot its activation you can use the command line tool snf-manage sendactivation
+For its activation you can use the command line tool snf-manage sendactivation
index d32e039..8ee3eb6 100644 (file)
@@ -52,6 +52,7 @@ from django.http import HttpResponseRedirect, HttpResponseBadRequest
 from django.db.utils import IntegrityError
 from django.contrib.auth.views import password_change
 from django.core.exceptions import ValidationError
+from django.db.models import Q
 
 from astakos.im.models import AstakosUser, Invitation, ApprovalTerms
 from astakos.im.activation_backends import get_backend, SimpleBackend
@@ -59,7 +60,7 @@ from astakos.im.util import get_context, prepare_response, set_cookie, get_query
 from astakos.im.forms import *
 from astakos.im.functions import send_greeting, send_feedback, SendMailError, \
     invite as invite_func, logout as auth_logout
-from astakos.im.settings import DEFAULT_CONTACT_EMAIL, DEFAULT_FROM_EMAIL, COOKIE_NAME, COOKIE_DOMAIN, IM_MODULES, SITENAME, LOGOUT_NEXT
+from astakos.im.settings import DEFAULT_CONTACT_EMAIL, DEFAULT_FROM_EMAIL, COOKIE_NAME, COOKIE_DOMAIN, IM_MODULES, SITENAME, LOGOUT_NEXT, LOGGING_LEVEL
 
 logger = logging.getLogger(__name__)
 
@@ -265,6 +266,9 @@ def edit_profile(request, template_name='im/profile.html', extra_context={}):
                 messages.add_message(request, messages.SUCCESS, msg)
             except ValueError, ve:
                 messages.add_message(request, messages.ERROR, ve)
+    elif request.method == "GET":
+        request.user.is_verified = True
+        request.user.save()
     return render_response(template_name,
                            reset_cookie = reset_cookie,
                            profile_form = form,
@@ -275,7 +279,7 @@ def signup(request, template_name='im/signup.html', on_success='im/signup_comple
     """
     Allows a user to create a local account.
 
-    In case of GET request renders a form for providing the user information.
+    In case of GET request renders a form for entering the user information.
     In case of POST handles the signup.
 
     The user activation will be delegated to the backend specified by the ``backend`` keyword argument
@@ -283,7 +287,7 @@ def signup(request, template_name='im/signup.html', on_success='im/signup_comple
     if settings.ASTAKOS_INVITATIONS_ENABLED is True or ``astakos.im.activation_backends.SimpleBackend`` if not
     (see activation_backends);
     
-    Upon successful user creation if ``next`` url parameter is present the user is redirected there
+    Upon successful user creation, if ``next`` url parameter is present the user is redirected there
     otherwise renders the same page with a success message.
     
     On unsuccessful creation, renders ``template_name`` with an error message.
@@ -294,7 +298,6 @@ def signup(request, template_name='im/signup.html', on_success='im/signup_comple
         A custom template to render. This is optional;
         if not specified, this will default to ``im/signup.html``.
 
-
     ``on_success``
         A custom template to render in case of success. This is optional;
         if not specified, this will default to ``im/signup_complete.html``.
@@ -308,7 +311,7 @@ def signup(request, template_name='im/signup.html', on_success='im/signup_comple
     im/signup_complete.html or ``on_success`` keyword argument. 
     """
     if request.user.is_authenticated():
-        return HttpResponseRedirect(reverse('astakos.im.views.index'))
+        return HttpResponseRedirect(reverse('astakos.im.views.edit_profile'))
     
     provider = get_query(request).get('provider', 'local')
     try:
@@ -330,6 +333,8 @@ def signup(request, template_name='im/signup.html', on_success='im/signup_comple
                     additional_email = form.cleaned_data['additional_email']
                     if additional_email != user.email:
                         user.additionalmail_set.create(email=additional_email)
+                        msg = 'Additional email: %s saved for user %s.' % (additional_email, user.email)
+                        logger._log(LOGGING_LEVEL, msg, [])
                 if user and user.is_active:
                     next = request.POST.get('next', '')
                     return prepare_response(request, user, next=next)
@@ -387,7 +392,7 @@ def feedback(request, template_name='im/feedback.html', email_template_name='im/
 
         form = FeedbackForm(request.POST)
         if form.is_valid():
-            msg = form.cleaned_data['feedback_msg'],
+            msg = form.cleaned_data['feedback_msg']
             data = form.cleaned_data['feedback_data']
             try:
                 send_feedback(msg, data, request.user, email_template_name)
@@ -406,9 +411,11 @@ def logout(request, template='registration/logged_out.html', extra_context={}):
     """
     Wraps `django.contrib.auth.logout` and delete the cookie.
     """
+    msg = 'Cookie deleted for %s' % (request.user.email)
     auth_logout(request)
     response = HttpResponse()
     response.delete_cookie(COOKIE_NAME, path='/', domain=COOKIE_DOMAIN)
+    logger._log(LOGGING_LEVEL, msg, [])
     next = request.GET.get('next')
     if next:
         response['Location'] = next
@@ -439,8 +446,13 @@ def activate(request, email_template_name='im/welcome_email.txt', on_failure='im
     except AstakosUser.DoesNotExist:
         return HttpResponseBadRequest(_('No such user'))
     
+    if user.is_active:
+        message = 'Account already active.'
+        messages.add_message(request, messages.ERROR, message)
+        return render_response(on_failure)
+    
     try:
-        local_user = AstakosUser.objects.get(email=user.email, is_active=True)
+        local_user = AstakosUser.objects.get(~Q(id = user.id), email=user.email, is_active=True)
     except AstakosUser.DoesNotExist:
         user.is_active = True
         user.email_verified = True
@@ -450,16 +462,17 @@ def activate(request, email_template_name='im/welcome_email.txt', on_failure='im
             return HttpResponseBadRequest(e)
     else:
         # switch the existing account to shibboleth one
-        local_user.provider = 'shibboleth'
-        local_user.set_unusable_password()
-        local_user.third_party_identifier = user.third_party_identifier
-        try:
-            local_user.save()
-        except ValidationError, e:
-            return HttpResponseBadRequest(e)
-        user.delete()
-        user = local_user
-    
+        if user.provider == 'shibboleth':
+            local_user.provider = 'shibboleth'
+            local_user.set_unusable_password()
+            local_user.third_party_identifier = user.third_party_identifier
+            try:
+                local_user.save()
+            except ValidationError, e:
+                return HttpResponseBadRequest(e)
+            user.delete()
+            user = local_user
+        
     try:
         send_greeting(user, email_template_name)
         response = prepare_response(request, user, next, renew=True)