Skip to content

Commit

Permalink
Merge 2ec25cb into 87a7f88
Browse files Browse the repository at this point in the history
  • Loading branch information
pszpetkowski committed Sep 9, 2016
2 parents 87a7f88 + 2ec25cb commit 91c8857
Show file tree
Hide file tree
Showing 19 changed files with 150 additions and 31 deletions.
21 changes: 14 additions & 7 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,32 @@ python:
- "2.6"
- "2.7"
- "3.4"
- "3.5"

env:
- DJANGO_VERSION=1.5.12 DRF_VERSION=2.4.5
- DJANGO_VERSION=1.6.11 DRF_VERSION=2.4.5
- DJANGO_VERSION=1.7.8 DRF_VERSION=2.4.5
- DJANGO_VERSION=1.5.12 DRF_VERSION=3.1.3
- DJANGO_VERSION=1.6.11 DRF_VERSION=3.1.3
- DJANGO_VERSION=1.7.8 DRF_VERSION=3.1.3
- DJANGO_VERSION=1.8.2 DRF_VERSION=3.1.3
- DJANGO_VERSION=1.5.12 DRF_VERSION=3.4.6
- DJANGO_VERSION=1.6.11 DRF_VERSION=3.4.6
- DJANGO_VERSION=1.7.8 DRF_VERSION=3.4.6
- DJANGO_VERSION=1.8.14 DRF_VERSION=3.4.6
- DJANGO_VERSION=1.9.9 DRF_VERSION=3.4.6
- DJANGO_VERSION=1.10.1 DRF_VERSION=3.4.6

matrix:
exclude:
# Django 1.7 and 1.8 dropped support for Python 2.6.
# Python 2.6 support has been dropped since Django 1.7.
- python: "2.6"
env: DJANGO_VERSION=1.7.8 DRF_VERSION=2.4.5
- python: "2.6"
env: DJANGO_VERSION=1.7.8 DRF_VERSION=3.1.3
env: DJANGO_VERSION=1.7.8 DRF_VERSION=3.4.6
- python: "2.6"
env: DJANGO_VERSION=1.8.2 DRF_VERSION=3.1.3
env: DJANGO_VERSION=1.8.14 DRF_VERSION=3.4.6
- python: "2.6"
env: DJANGO_VERSION=1.9.9 DRF_VERSION=3.4.6
- python: "2.6"
env: DJANGO_VERSION=1.10.1 DRF_VERSION=3.4.6

install:
- pip install -q Django==$DJANGO_VERSION
Expand Down
6 changes: 4 additions & 2 deletions multitoken/admin.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from django.contrib import admin
from . import models

import swapper
Token = swapper.load_model('multitoken', 'Token')


class TokenAdmin(admin.ModelAdmin):
Expand All @@ -9,4 +11,4 @@ class TokenAdmin(admin.ModelAdmin):
readonly_fields = ('created',)


admin.site.register(models.Token, TokenAdmin)
admin.site.register(Token, TokenAdmin)
5 changes: 3 additions & 2 deletions multitoken/authentication.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from rest_framework import authentication
from . import models
import swapper
Token = swapper.load_model('multitoken', 'Token')


class TokenAuthentication(authentication.TokenAuthentication):
model = models.Token
model = Token
19 changes: 19 additions & 0 deletions multitoken/migrations/0002_auto_20160909_0303.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.1 on 2016-09-09 03:03
from __future__ import unicode_literals

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('multitoken', '0001_initial'),
]

operations = [
migrations.AlterUniqueTogether(
name='token',
unique_together=set([]),
),
]
21 changes: 21 additions & 0 deletions multitoken/migrations/0003_auto_20160909_1217.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.1 on 2016-09-09 12:17
from __future__ import unicode_literals

from django.conf import settings
from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('multitoken', '0002_auto_20160909_0303'),
]

operations = [
migrations.AlterUniqueTogether(
name='token',
unique_together=set([('user', 'client')]),
),
]
16 changes: 14 additions & 2 deletions multitoken/models.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import binascii
import os

from django.conf import settings
from django.db import models

import swapper


class Token(models.Model):
class BaseToken(models.Model):
key = models.CharField(max_length=40, primary_key=True)
user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='auth_tokens')
client = models.CharField(max_length=255)
Expand All @@ -15,11 +18,12 @@ class Meta:
'user',
'client',
)
abstract = True

def save(self, *args, **kwargs):
if not self.key:
self.key = self.generate_key()
return super(Token, self).save(*args, **kwargs)
return super(BaseToken, self).save(*args, **kwargs)

def generate_key(self):
return binascii.hexlify(os.urandom(20)).decode()
Expand All @@ -30,3 +34,11 @@ def __unicode__(self):
LOGIN_FIELDS = (
'client',
)


class Token(BaseToken):
# todo: add more fields to default implementation?

class Meta(BaseToken.Meta):
swappable = swapper.swappable_setting('multitoken', 'Token')
abstract = False
7 changes: 5 additions & 2 deletions multitoken/serializers.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
from django.contrib.auth import get_user_model, authenticate
from rest_framework import serializers
from . import models, constants
from . import constants

import swapper
Token = swapper.load_model('multitoken', 'Token')

User = get_user_model()

Expand All @@ -9,7 +12,7 @@ class TokenSerializer(serializers.ModelSerializer):
auth_token = serializers.CharField(source='key')

class Meta:
model = models.Token
model = Token
fields = (
'auth_token',
)
Expand Down
6 changes: 3 additions & 3 deletions multitoken/urls.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from django.conf.urls import patterns, url
from django.conf.urls import url
from . import views

urlpatterns = patterns('',
urlpatterns = [
url(r'^obtain-token/$', views.ObtainTokenView.as_view(), name='obtain_token'),
url(r'^invalidate-token/$', views.InvalidateTokenView.as_view(), name='invalidate_token'),
)
]
7 changes: 4 additions & 3 deletions multitoken/utils.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from . import models
import swapper
Token = swapper.load_model('multitoken', 'Token')


def get_or_create_token(user, serializer_data=None):
serializer_data = serializer_data or {}
token_data = dict((k, v) for k, v in serializer_data.items() if k in models.Token.LOGIN_FIELDS)
token, _ = models.Token.objects.get_or_create(user=user, **token_data)
token_data = dict((k, v) for k, v in serializer_data.items() if k in Token.LOGIN_FIELDS)
token, _ = Token.objects.get_or_create(user=user, **token_data)
return token
2 changes: 1 addition & 1 deletion multitoken/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class ObtainTokenView(generics.GenericAPIView):
token_serializer_class = serializers.TokenSerializer

def post(self, request):
serializer = self.get_serializer(data=request.DATA)
serializer = self.get_serializer(data=request.data)
if serializer.is_valid():
return self.login(serializer)
else:
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
Django>=1.5
djangorestframework>=2.4.5
swapper>=1.0.0
Empty file added testproject/__init__.py
Empty file.
2 changes: 1 addition & 1 deletion testproject/manage.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import sys

if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings")
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "testapp.settings")

from django.core.management import execute_from_command_line

Expand Down
35 changes: 35 additions & 0 deletions testproject/testapp/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.1 on 2016-09-09 12:18
from __future__ import unicode_literals

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

initial = True

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]

operations = [
migrations.CreateModel(
name='TestToken',
fields=[
('key', models.CharField(max_length=40, primary_key=True, serialize=False)),
('client', models.CharField(max_length=255)),
('created', models.DateTimeField(auto_now_add=True)),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='auth_tokens', to=settings.AUTH_USER_MODEL)),
],
options={
'abstract': False,
},
),
migrations.AlterUniqueTogether(
name='testtoken',
unique_together=set([('user', 'client')]),
),
]
Empty file.
5 changes: 4 additions & 1 deletion testproject/testapp/models.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
from django.db import models
from multitoken import models

# Create your models here.

class TestToken(models.BaseToken):
pass
6 changes: 4 additions & 2 deletions testproject/settings.py → testproject/testapp/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

DEBUG = True

BASE_DIR = os.path.dirname(__file__)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

DATABASES = {
'default': {
Expand Down Expand Up @@ -46,4 +46,6 @@
),
}

ROOT_URLCONF = 'urls'
ROOT_URLCONF = 'testapp.urls'

MULTITOKEN_TOKEN_MODEL = 'testapp.TestToken'
22 changes: 17 additions & 5 deletions testproject/testapp/tests.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
from django.contrib.auth import get_user_model
from django.contrib.auth.signals import user_logged_in
from django.test import TestCase
from djet import assertions, restframework, utils
from rest_framework import status
from multitoken import models, views

from multitoken import views
from . import settings

import swapper
Token = swapper.load_model('multitoken', 'Token')


def create_user(**kwargs):
Expand Down Expand Up @@ -61,7 +67,7 @@ def test_post_should_not_login_inactive_user(self):
response = self.view(request)

self.assert_status_equal(response, status.HTTP_400_BAD_REQUEST)
with self.assertRaises(models.Token.DoesNotExist):
with self.assertRaises(Token.DoesNotExist):
user.auth_tokens.get()
self.assertFalse(self.signal_sent)

Expand All @@ -78,7 +84,7 @@ def test_post_should_not_login_when_wrong_credentials(self):
response = self.view(request)

self.assert_status_equal(response, status.HTTP_400_BAD_REQUEST)
with self.assertRaises(models.Token.DoesNotExist):
with self.assertRaises(Token.DoesNotExist):
user.auth_tokens.get()
self.assertFalse(self.signal_sent)

Expand All @@ -89,14 +95,14 @@ class LogoutViewTest(restframework.APIViewTestCase,

def test_post_should_logout_logged_in_user(self):
user = create_user()
token = models.Token.objects.create(user=user, client='my-device')
token = Token.objects.create(user=user, client='my-device')

request = self.factory.post(user=user, token=token)
response = self.view(request)

self.assert_status_equal(response, status.HTTP_200_OK)
self.assertEqual(response.data, None)
with self.assertRaises(models.Token.DoesNotExist):
with self.assertRaises(Token.DoesNotExist):
utils.refresh(token)

def test_post_should_deny_logging_out_when_user_not_logged_in(self):
Expand All @@ -106,3 +112,9 @@ def test_post_should_deny_logging_out_when_user_not_logged_in(self):
response = self.view(request)

self.assert_status_equal(response, status.HTTP_401_UNAUTHORIZED)


class SwappableTokenTest(TestCase):
def test_is_token_model_swapped(self):
swapped_token_model = swapper.is_swapped('multitoken', 'Token')
self.assertEqual(swapped_token_model, settings.MULTITOKEN_TOKEN_MODEL)
File renamed without changes.

0 comments on commit 91c8857

Please sign in to comment.