Skip to content

Commit

Permalink
Merge pull request #48 from groovecoder/get-email-from-github-21
Browse files Browse the repository at this point in the history
fix #21 - Get email from github
  • Loading branch information
jsatt committed Nov 10, 2014
2 parents fccc629 + 4140c91 commit 5c5cc70
Show file tree
Hide file tree
Showing 7 changed files with 208 additions and 4 deletions.
33 changes: 33 additions & 0 deletions codesy/adapters.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from django.forms import ValidationError

from allauth.account.adapter import DefaultAccountAdapter
from allauth.socialaccount.adapter import DefaultSocialAccountAdapter


REMOVE_MESSAGE = 'You must keep at least one connected account.'


class CodesyAccountAdapter(DefaultAccountAdapter):

def is_open_for_signup(self, request):
"""
Disable regular signup to require social signup.
See https://github.com/pennersr/django-allauth/issues/345
"""
return False


class CodesySocialAccountAdapter(DefaultSocialAccountAdapter):

def is_open_for_signup(self, request, sociallogin):
"""
Enable social signup.
"""
return True

def validate_disconnect(self, account, accounts):
"""
Don't let users disconnect their last social account.
"""
if len(accounts) == 1:
raise ValidationError(REMOVE_MESSAGE)
23 changes: 23 additions & 0 deletions codesy/base/models.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,32 @@
import requests

from django.contrib.auth.models import AbstractUser
from django.db import models
from django.dispatch import receiver

from allauth.account.signals import user_signed_up


EMAIL_URL = 'https://api.github.com/user/emails'


class User(AbstractUser):
balanced_card_href = models.CharField(max_length=100, blank=True)
balanced_bank_account_href = models.CharField(max_length=100, blank=True)

USERNAME_FIELD = 'username'


@receiver(user_signed_up)
def add_email_from_signup(sender, request, user, **kwargs):
params = {'access_token': kwargs['sociallogin'].token}
email_data = requests.get(EMAIL_URL, params=params).json()
if email_data:
verified_emails = [e for e in email_data if e['verified']]
if not verified_emails:
return None
sorted_emails = sorted(verified_emails,
key=lambda e: (e['primary'], e['verified']),
reverse=True)
user.email = sorted_emails[0]['email']
user.save(update_fields=['email'])
10 changes: 6 additions & 4 deletions codesy/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,16 +82,18 @@
)

# auth and allauth settings
ACCOUNT_ADAPTER = 'codesy.adapters.CodesyAccountAdapter'
ACCOUNT_EMAIL_VERIFICATION = os.environ.get('ACCOUNT_EMAIL_VERIFICATION',
'optional')
AUTH_USER_MODEL = 'base.User'
LOGIN_REDIRECT_URL = '/'
SOCIALACCOUNT_ADAPTER = 'codesy.adapters.CodesySocialAccountAdapter'
SOCIALACCOUNT_QUERY_EMAIL = True
SOCIALACCOUNT_PROVIDERS = {
'github': {
'SCOPE': ['email'],
'SCOPE': ['user:email'],
}
}
AUTH_USER_MODEL = 'base.User'
ACCOUNT_EMAIL_VERIFICATION = os.environ.get('ACCOUNT_EMAIL_VERIFICATION',
'optional')

ROOT_URLCONF = 'codesy.urls'

Expand Down
Empty file added codesy/tests/__init__.py
Empty file.
36 changes: 36 additions & 0 deletions codesy/tests/adapters_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import fudge

from django.forms import ValidationError

from django.test import TestCase
from django.test.client import RequestFactory

from codesy.adapters import CodesyAccountAdapter, CodesySocialAccountAdapter


class AdaptersTest(TestCase):
"""
Verify our adapters are wired correctly.
"""
def setUp(self):
self.csaa = CodesySocialAccountAdapter()
self.request = RequestFactory()
self.sociallogin = fudge.Fake()

def test_account_adapter_disabled(self):
caa = CodesyAccountAdapter()
self.assertEquals(False, caa.is_open_for_signup(self.request))

def test_social_adapter_enabled(self):
self.assertEquals(True, self.csaa.is_open_for_signup(self.request,
self.sociallogin))

def test_social_adapter_prohibits_disconnecting_last_account(self):
account = fudge.Fake()
account2 = fudge.Fake()
single_account_list = [account, ]
multi_account_list = [account, account2]
self.csaa.validate_disconnect(account, multi_account_list)

with self.assertRaises(ValidationError):
self.csaa.validate_disconnect(account, single_account_list)
103 changes: 103 additions & 0 deletions codesy/tests/model_tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
from django.test import TestCase
from django.test.client import RequestFactory

import fudge

from ..base.models import EMAIL_URL, User, add_email_from_signup


VERIFIED_PRIMARY_EMAIL = "verified+primary@test.com"
VERIFIED_EMAIL = "verified@test.com"
UNVERIFIED_EMAIL = "unverified@test.com"

GITHUB_DATA_WITH_VERIFIED_PRIMARY_EMAIL = [
{u'verified': True,
u'email': u'%s' % VERIFIED_PRIMARY_EMAIL,
u'primary': True}
]
GITHUB_DATA_WITH_VERIFIED_EMAIL = [
{u'verified': True,
u'email': u'%s' % VERIFIED_EMAIL,
u'primary': False}
]
GITHUB_DATA_WITH_UNVERIFIED_EMAIL = [
{u'verified': False,
u'email': u'%s' % UNVERIFIED_EMAIL,
u'primary': False}
]
GITHUB_DATA_WITH_MANY_EMAILS = (GITHUB_DATA_WITH_UNVERIFIED_EMAIL +
GITHUB_DATA_WITH_VERIFIED_PRIMARY_EMAIL +
GITHUB_DATA_WITH_VERIFIED_EMAIL)


class SignUpReceiverTest(TestCase):
def setUp(self):
self.sociallogin = fudge.Fake().has_attr(token='12345')
self.params = {'access_token': self.sociallogin.token}
self.sender = User
self.request = RequestFactory()
self.user = (fudge.Fake('User')
.has_attr(email=None)
.expects('save'))
self.kwargs = {'sociallogin': self.sociallogin}
self.fake_get = fudge.Fake('requests.get')

@fudge.patch('requests.get')
def test_verified_primary_email_from_github_api(self, fake_get):
(fake_get.expects_call()
.with_args(EMAIL_URL, params=self.params)
.returns_fake()
.expects('json')
.returns(
GITHUB_DATA_WITH_VERIFIED_PRIMARY_EMAIL))

add_email_from_signup(self.sender,
self.request,
self.user,
**self.kwargs)
self.assertEquals(VERIFIED_PRIMARY_EMAIL, self.user.email)

@fudge.patch('requests.get')
def test_verified_email_from_github_api(self, fake_get):
(fake_get.expects_call()
.with_args(EMAIL_URL, params=self.params)
.returns_fake()
.expects('json')
.returns(
GITHUB_DATA_WITH_VERIFIED_EMAIL))

add_email_from_signup(self.sender,
self.request,
self.user,
**self.kwargs)
self.assertEquals(VERIFIED_EMAIL, self.user.email)

@fudge.patch('requests.get')
def test_unverified_email_from_github_api(self, fake_get):
(fake_get.expects_call()
.with_args(EMAIL_URL, params=self.params)
.returns_fake()
.expects('json')
.returns(
GITHUB_DATA_WITH_UNVERIFIED_EMAIL))

add_email_from_signup(self.sender,
self.request,
self.user,
**self.kwargs)
self.assertEquals(None, self.user.email)

@fudge.patch('requests.get')
def test_many_emails_from_github_api(self, fake_get):
(fake_get.expects_call()
.with_args(EMAIL_URL, params=self.params)
.returns_fake()
.expects('json')
.returns(
GITHUB_DATA_WITH_MANY_EMAILS))

add_email_from_signup(self.sender,
self.request,
self.user,
**self.kwargs)
self.assertEquals(VERIFIED_PRIMARY_EMAIL, self.user.email)
7 changes: 7 additions & 0 deletions codesy/tests/view_tests.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
from django.conf import settings
from django.contrib.auth.models import AnonymousUser
from django.core.urlresolvers import reverse
from django.test import TestCase
from model_mommy import mommy
import fudge

from codesy import base, serializers, views


class AuthViewTest(TestCase):
def test_allauth_signup_disabled(self):
resp = self.client.get(reverse("account_signup"))
self.assertContains(resp, 'Sign Up Closed')


class UserViewSetTest(TestCase):
def setUp(self):
self.view = views.UserViewSet()
Expand Down

0 comments on commit 5c5cc70

Please sign in to comment.