Skip to content

Commit

Permalink
Adds support for Django 5 and Moves to Github Actions for core CI/CD (#…
Browse files Browse the repository at this point in the history
…615)

* Support Django5+

* Test GHA

* Adds tests

* Updates tox to test Django 5

* Corrects Python version

* Adds support for Django 5 and Switches to Github Actions

---------

Co-authored-by: q0w <[email protected]>
  • Loading branch information
iMerica and q0w authored Apr 16, 2024
1 parent 069cd11 commit b608360
Show file tree
Hide file tree
Showing 21 changed files with 131 additions and 124 deletions.
46 changes: 0 additions & 46 deletions .circleci/config.yml

This file was deleted.

5 changes: 5 additions & 0 deletions .flake8
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[flake8]
per-file-ignores =
dj_rest_auth/tests/test_serializers.py:E501,F401
dj_rest_auth/serializers.py:E501
dj_rest_auth/jwt_auth.py:E501
91 changes: 83 additions & 8 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -1,14 +1,89 @@
name: Release to PyPi
on: [push]
name: Lint, Build and Test
on:
push:
branches:
- master
pull_request:
branches:
- master

jobs:
lint:
runs-on: ubuntu-latest
name: Lint
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Python 3.8
uses: actions/setup-python@v5
with:
python-version: 3.8
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install flake8
- name: Lint
run: flake8 dj_rest_auth/ --append-config ./.flake8
build:
name: Publish
runs-on: ubuntu-latest
name: Build
needs: [lint]
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Python 3.8
uses: actions/setup-python@v5
with:
python-version: 3.8
- name: Install dependencies
run: |
python -m pip install --upgrade pip
- name: Build
run: python3 setup.py sdist
- name: Store artifacts
uses: actions/upload-artifact@v4
with:
name: dist
path: dist/
test:
runs-on: ubuntu-20.04
name: Test Python ${{ matrix.python-version }} + Django ~= ${{ matrix.django-version }}
needs: [build]
strategy:
matrix:
python-version: ['3.8', '3.9', '3.10', '3.11']
django-version: ['3.2', '4.2', '5.0']
exclude:
- python-version: '3.8'
django-version: '5.0'
- python-version: '3.9'
django-version: '5.0'
steps:
- name: Publish package
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
uses: pypa/gh-action-pypi-publish@master
- name: Checkout
uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
pip install -r dj_rest_auth/tests/requirements.pip
pip install "Django~=${{ matrix.django-version }}.0"
- name: Run Tests
run: |
echo "$(python --version) / Django $(django-admin --version)"
coverage run ./runtests.py
- name: Generate Coverage Report
run: |
mkdir -p test-results/
coverage report
coverage xml
- name: Code Coverage Summary Report
uses: irongut/[email protected]
with:
filename: coverage.xml
- name: Store test results
uses: actions/upload-artifact@v4
with:
user: __token__
password: ${{ secrets.pypi_password }}
name: results-${{ matrix.python-version }}-${{ matrix.django-version }}
path: test-results/
28 changes: 0 additions & 28 deletions .github/workflows/stale.yml

This file was deleted.

6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
# Dj-Rest-Auth
[![<iMerica>](https://circleci.com/gh/iMerica/dj-rest-auth.svg?style=svg)](https://app.circleci.com/pipelines/github/iMerica/dj-rest-auth)
[![<iMerica>](https://github.com/iMerica/dj-rest-auth/actions/workflows/main.yml/badge.svg)](https://github.com/iMerica/dj-rest-auth/actions/workflows/main.yml/)


Drop-in API endpoints for handling authentication securely in Django Rest Framework. Works especially well
with SPAs (e.g., React, Vue, Angular), and Mobile applications.

## Requirements
- Django 2, 3, or 4 (See Unit Test Coverage in CI)
- Python 3
- Django 3, 4 and 5 (See Unit Test Coverage in CI)
- Python >= 3.8

## Quick Setup

Expand Down
2 changes: 1 addition & 1 deletion dj_rest_auth/__version__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
__title__ = 'dj-rest-auth'
__description__ = 'Authentication and Registration in Django Rest Framework.'
__url__ = 'http://github.com/iMerica/dj-rest-auth'
__version__ = '5.1.0'
__version__ = '6.0.0'
__author__ = '@iMerica https://github.com/iMerica'
__author_email__ = '[email protected]'
__license__ = 'MIT'
Expand Down
4 changes: 2 additions & 2 deletions dj_rest_auth/app_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
'JWT_AUTH_SECURE': False,
'JWT_AUTH_HTTPONLY': True,
'JWT_AUTH_SAMESITE': 'Lax',
'JWT_AUTH_COOKIE_DOMAIN' : None,
'JWT_AUTH_COOKIE_DOMAIN': None,
'JWT_AUTH_RETURN_EXPIRATION': False,
'JWT_AUTH_COOKIE_USE_CSRF': False,
'JWT_AUTH_COOKIE_ENFORCE_CSRF_ON_UNAUTHENTICATED': False,
Expand All @@ -59,7 +59,7 @@
)

# List of settings that have been removed
REMOVED_SETTINGS = ( )
REMOVED_SETTINGS = []


class APISettings(_APISettings): # pragma: no cover
Expand Down
3 changes: 1 addition & 2 deletions dj_rest_auth/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,7 @@ def save(self, request, **kwargs):
'uid': uid,
}
if (
allauth_account_settings.AUTHENTICATION_METHOD
!= allauth_account_settings.AuthenticationMethod.EMAIL
allauth_account_settings.AUTHENTICATION_METHOD != allauth_account_settings.AuthenticationMethod.EMAIL
):
context['username'] = user_username(user)
get_adapter(request).send_mail(
Expand Down
3 changes: 1 addition & 2 deletions dj_rest_auth/jwt_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ def set_jwt_access_cookie(response, access_token):
cookie_samesite = api_settings.JWT_AUTH_SAMESITE
cookie_domain = api_settings.JWT_AUTH_COOKIE_DOMAIN


if cookie_name:
response.set_cookie(
cookie_name,
Expand Down Expand Up @@ -139,7 +138,7 @@ def authenticate(self, request):
if header is None:
if cookie_name:
raw_token = request.COOKIES.get(cookie_name)
if api_settings.JWT_AUTH_COOKIE_ENFORCE_CSRF_ON_UNAUTHENTICATED: #True at your own risk
if api_settings.JWT_AUTH_COOKIE_ENFORCE_CSRF_ON_UNAUTHENTICATED: # True at your own risk
self.enforce_csrf(request)
elif raw_token is not None and api_settings.JWT_AUTH_COOKIE_USE_CSRF:
self.enforce_csrf(request)
Expand Down
5 changes: 3 additions & 2 deletions dj_rest_auth/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from .app_settings import api_settings


def get_token_model():
token_model = api_settings.TOKEN_MODEL
session_login = api_settings.SESSION_LOGIN
Expand All @@ -15,13 +16,13 @@ def get_token_model():
'more of `TOKEN_MODEL`, `USE_JWT` or `SESSION_LOGIN`'
)
if (
token_model == DefaultTokenModel
and 'rest_framework.authtoken' not in settings.INSTALLED_APPS
token_model == DefaultTokenModel and 'rest_framework.authtoken' not in settings.INSTALLED_APPS
):
raise ImproperlyConfigured(
'You must include `rest_framework.authtoken` in INSTALLED_APPS '
'or set TOKEN_MODEL to None'
)
return token_model


TokenModel = get_token_model()
3 changes: 1 addition & 2 deletions dj_rest_auth/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,7 @@ def validate_auth_user_status(user):
def validate_email_verification_status(user, email=None):
from allauth.account import app_settings as allauth_account_settings
if (
allauth_account_settings.EMAIL_VERIFICATION == allauth_account_settings.EmailVerificationMethod.MANDATORY
and not user.emailaddress_set.filter(email=user.email, verified=True).exists()
allauth_account_settings.EMAIL_VERIFICATION == allauth_account_settings.EmailVerificationMethod.MANDATORY and not user.emailaddress_set.filter(email=user.email, verified=True).exists()
):
raise serializers.ValidationError(_('E-mail is not verified.'))

Expand Down
3 changes: 1 addition & 2 deletions dj_rest_auth/tests/requirements.pip
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
coveralls==1.11.1
django-allauth==0.61.1
django>=2.2,<5.0
djangorestframework-simplejwt==4.6.0
djangorestframework-simplejwt>=5.3.1
flake8==3.8.4
responses==0.12.1
unittest-xml-reporting==3.0.4
19 changes: 9 additions & 10 deletions dj_rest_auth/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -501,7 +501,7 @@ def test_registration_allowed_with_custom_no_password_serializer(self):
self.assertEqual(new_user.username, payload['username'])
self.assertFalse(new_user.has_usable_password())

## Also check that regular registration also works
# Also check that regular registration also works
user_count = get_user_model().objects.all().count()

# test empty payload
Expand All @@ -514,7 +514,6 @@ def test_registration_allowed_with_custom_no_password_serializer(self):
new_user = get_user_model().objects.latest('id')
self.assertEqual(new_user.username, self.REGISTRATION_DATA['username'])


@override_api_settings(USE_JWT=True)
def test_registration_with_jwt(self):
user_count = get_user_model().objects.all().count()
Expand Down Expand Up @@ -837,15 +836,15 @@ def test_wo_csrf_enforcement(self):
self.assertTrue('jwt-auth' in list(client.cookies.keys()))
self.assertEquals(resp.status_code, 200)

## TEST WITH JWT AUTH HEADER
# TEST WITH JWT AUTH HEADER
jwtclient = APIClient(enforce_csrf_checks=True)
token = resp.data['access']
resp = jwtclient.get('/protected-view/', HTTP_AUTHORIZATION='Bearer ' + token)
self.assertEquals(resp.status_code, 200)
resp = jwtclient.post('/protected-view/', {}, HTTP_AUTHORIZATION='Bearer ' + token)
self.assertEquals(resp.status_code, 200)

## TEST WITH COOKIES
# TEST WITH COOKIES
resp = client.get('/protected-view/')
self.assertEquals(resp.status_code, 200)

Expand Down Expand Up @@ -883,7 +882,7 @@ def test_csrf_wo_login_csrf_enforcement(self):
self.assertTrue('csrftoken' in list(client.cookies.keys()))
self.assertEquals(resp.status_code, 200)

## TEST WITH JWT AUTH HEADER
# TEST WITH JWT AUTH HEADER
jwtclient = APIClient(enforce_csrf_checks=True)
token = resp.data['access']
resp = jwtclient.get('/protected-view/')
Expand All @@ -909,7 +908,7 @@ def test_csrf_wo_login_csrf_enforcement(self):
@override_api_settings(USE_JWT=True)
@override_api_settings(JWT_AUTH_COOKIE='jwt-auth')
@override_api_settings(JWT_AUTH_COOKIE_USE_CSRF=True)
@override_api_settings(JWT_AUTH_COOKIE_ENFORCE_CSRF_ON_UNAUTHENTICATED=True) # True at your own risk
@override_api_settings(JWT_AUTH_COOKIE_ENFORCE_CSRF_ON_UNAUTHENTICATED=True) # True at your own risk
@override_settings(
REST_FRAMEWORK=dict(
DEFAULT_AUTHENTICATION_CLASSES=[
Expand Down Expand Up @@ -942,9 +941,9 @@ def test_csrf_w_login_csrf_enforcement(self):
self.assertTrue('csrftoken' in list(client.cookies.keys()))
self.assertEquals(resp.status_code, 200)

## TEST WITH JWT AUTH HEADER does not make sense
# TEST WITH JWT AUTH HEADER does not make sense

## TEST WITH COOKIES
# TEST WITH COOKIES
resp = client.get('/protected-view/')
self.assertEquals(resp.status_code, 200)
# fail w/o csrftoken in payload
Expand All @@ -958,7 +957,7 @@ def test_csrf_w_login_csrf_enforcement(self):
@override_api_settings(USE_JWT=True)
@override_api_settings(JWT_AUTH_COOKIE='jwt-auth')
@override_api_settings(JWT_AUTH_COOKIE_USE_CSRF=False)
@override_api_settings(JWT_AUTH_COOKIE_ENFORCE_CSRF_ON_UNAUTHENTICATED=True) # True at your own risk
@override_api_settings(JWT_AUTH_COOKIE_ENFORCE_CSRF_ON_UNAUTHENTICATED=True) # True at your own risk
@override_settings(
REST_FRAMEWORK=dict(
DEFAULT_AUTHENTICATION_CLASSES=[
Expand Down Expand Up @@ -1064,7 +1063,7 @@ def test_custom_token_refresh_view(self):
# Ensure access keys are provided in response
self.assertIn('access', refresh_resp.data)
self.assertIn('access_expiration', refresh_resp.data)

@override_api_settings(JWT_AUTH_RETURN_EXPIRATION=True)
@override_api_settings(USE_JWT=True)
@override_api_settings(JWT_AUTH_COOKIE='xxx')
Expand Down
2 changes: 1 addition & 1 deletion dj_rest_auth/tests/test_serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from allauth.socialaccount.providers.facebook.views import FacebookOAuth2Adapter
from allauth.socialaccount.providers.facebook.views import FacebookProvider
from allauth.socialaccount.models import SocialApp
from allauth.exceptions import ImmediateHttpResponse
from allauth.core.exceptions import ImmediateHttpResponse
from django.contrib.auth import get_user_model
from django.urls import reverse
from django.core.exceptions import ValidationError
Expand Down
1 change: 0 additions & 1 deletion dj_rest_auth/tests/test_social.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import responses
from allauth.socialaccount.models import SocialApp
from allauth.socialaccount.providers.facebook.provider import GRAPH_API_URL
from django.conf import settings
from django.contrib.auth import get_user_model
from django.contrib.sites.models import Site
from django.test import TestCase
Expand Down
Loading

0 comments on commit b608360

Please sign in to comment.