Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create django.yml #4

Open
wants to merge 34 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
b8b9741
Add todo apis without authentication
pyungjae-park Jul 31, 2022
c15b222
Resolve cors issue
pyungjae-park Jul 31, 2022
927b252
Add todo checking api
pyungjae-park Jul 31, 2022
3801117
change text field of Todo into content
young924 Aug 4, 2022
8c846c9
Fix variable names
young924 Aug 5, 2022
0a8b835
merge and update setting
hyemmie Aug 13, 2022
2e80bda
add accounts path
hyemmie Aug 13, 2022
8fff7ae
impl accounts app
hyemmie Aug 13, 2022
14e7c28
edit todo author to custom user
hyemmie Aug 13, 2022
dbc907c
add migrations
hyemmie Aug 13, 2022
c02e85c
remove useless exception
hyemmie Aug 13, 2022
54d4205
Change status codes (#3)
young924 Aug 24, 2022
6964580
Merge branch 'seminar/week-3' of https://github.com/snulion10thReactS…
hyemmie Sep 4, 2022
aed3f3b
remove email field at user
hyemmie Sep 5, 2022
2a93ccb
edit serializer name
hyemmie Sep 15, 2022
7bbea78
set token at response cookie
hyemmie Sep 15, 2022
0c6d6dc
Change content into text
young924 Sep 17, 2022
d4748c4
Register Todo to admin
young924 Sep 17, 2022
7ec9ad1
Merge branch 'seminar/week-3' of https://github.com/snulion10thReactS…
hyemmie Sep 21, 2022
1bfa0b5
fix logout with blacklisted token error bug
hyemmie Sep 21, 2022
fcf01cd
feat response with jwt cookie
hyemmie Sep 21, 2022
22344fe
feat authentication & authorization
hyemmie Sep 21, 2022
50c7017
update migrations
hyemmie Sep 21, 2022
1c4cddc
update requirements.txt
hyemmie Sep 21, 2022
11775ae
Fix todo delete view
young924 Sep 30, 2022
8b105ab
Merge branch 'seminar/week-3' of https://github.com/snulion10thReactS…
hyemmie Oct 6, 2022
dbb0c3a
add lifetime comment
hyemmie Oct 6, 2022
5110700
add empty authentication classes
hyemmie Oct 7, 2022
febcba1
Create django.yml
hyemmie Nov 16, 2022
afe9204
Merge branch 'main' of https://github.com/snulion10thReactSeminar/tod…
hyemmie Nov 16, 2022
2cca13d
update python ver
hyemmie Nov 16, 2022
d8df8a9
Merge branch 'advanced-seminar/week4' of https://github.com/snulion10…
hyemmie Nov 16, 2022
cf3a783
delete django.yml
hyemmie Nov 16, 2022
15729f7
update django key
hyemmie Nov 16, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/workflows/django-ci.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
name: Django CI
env:
DJANGO_KEY: ${{ secrets.django_key }}

on:
push:
Expand All @@ -13,7 +15,7 @@ jobs:
strategy:
max-parallel: 4
matrix:
python-version: [3.7, 3.8, 3.9]
python-version: [3.8]

steps:
- uses: actions/checkout@v3
Expand Down
1 change: 1 addition & 0 deletions .python-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
todolist
3 changes: 0 additions & 3 deletions account/admin.py

This file was deleted.

3 changes: 0 additions & 3 deletions account/models.py

This file was deleted.

3 changes: 0 additions & 3 deletions account/views.py

This file was deleted.

File renamed without changes.
4 changes: 4 additions & 0 deletions accounts/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from django.contrib import admin
from .models import User

admin.site.register(User)
4 changes: 2 additions & 2 deletions todolist/apps.py → accounts/apps.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from django.apps import AppConfig


class TodolistConfig(AppConfig):
class AccountsConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'todolist'
name = 'accounts'
16 changes: 16 additions & 0 deletions accounts/authenticate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from django.conf import settings
from rest_framework_simplejwt.authentication import JWTAuthentication

class CustomAuthentication(JWTAuthentication):
def authenticate(self, request):
header = self.get_header(request)

if header is None:
raw_token = request.COOKIES.get(settings.SIMPLE_JWT['ACCESS_TOKEN']) or None
else:
raw_token = self.get_raw_token(header)
if raw_token is None:
return None

validated_token = self.get_validated_token(raw_token)
return self.get_user(validated_token), validated_token
44 changes: 44 additions & 0 deletions accounts/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Generated by Django 4.0.6 on 2022-09-21 11:06

import django.contrib.auth.models
import django.contrib.auth.validators
from django.db import migrations, models
import django.utils.timezone


class Migration(migrations.Migration):

initial = True

dependencies = [
('auth', '0012_alter_user_first_name_max_length'),
]

operations = [
migrations.CreateModel(
name='User',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('password', models.CharField(max_length=128, verbose_name='password')),
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')),
('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')),
('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')),
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')),
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')),
],
options={
'verbose_name': 'user',
'verbose_name_plural': 'users',
'abstract': False,
},
managers=[
('objects', django.contrib.auth.models.UserManager()),
],
),
]
File renamed without changes.
15 changes: 15 additions & 0 deletions accounts/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from django.db import models
from django.contrib.auth.models import AbstractUser
from rest_framework_simplejwt.tokens import RefreshToken

class User(AbstractUser):

def __str__(self):
return self.username

def tokens(self):
refresh = RefreshToken.for_user(self)
return {
'refresh': str(refresh),
'access': str(refresh.access_token)
}
67 changes: 67 additions & 0 deletions accounts/serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
from rest_framework import serializers
from .models import User
from django.contrib import auth
from rest_framework.exceptions import AuthenticationFailed
from rest_framework_simplejwt.tokens import RefreshToken, TokenError


class SignUpSerializer(serializers.ModelSerializer):
password = serializers.CharField(max_length=68, min_length=8, write_only=True)
class Meta:
model = User
fields = ['username', 'password']

def validate(self, attrs):
username = attrs.get('username', '')
if not username.isalnum():
raise serializers.ValidationError(self.default_error_messages)
return attrs

def create(self, validated_data):
return User.objects.create_user(**validated_data)

class LoginSerializer(serializers.ModelSerializer):
password = serializers.CharField(max_length=68, min_length=8, write_only=True)
username = serializers.CharField(max_length=255, min_length=4)
tokens = serializers.SerializerMethodField()

def get_tokens(self, obj):
user = User.objects.get(username=obj['username'])
return {
'refresh': user.tokens()['refresh'],
'access': user.tokens()['access']
}

class Meta:
model = User
fields = ['password', 'username', 'tokens',]

def validate(self, attrs):
username = attrs.get('username', '')
password = attrs.get('password', '')
user = auth.authenticate(username=username, password=password)

if not user:
raise AuthenticationFailed('invalid credentials, try again')
if not user.is_active:
raise AuthenticationFailed('user not active')

return {
'username': user.username,
'tokens': user.tokens
}

class LogoutSerializer(serializers.Serializer):
refresh = serializers.CharField()

def validate(self, attrs):
self.token = attrs['refresh']
return attrs

def save(self, **kwargs):
try:
RefreshToken(self.token).blacklist()
except TokenError:
msg = 'Token is blacklisted'
raise serializers.ValidationError(msg, code='authorization')

File renamed without changes.
14 changes: 14 additions & 0 deletions accounts/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from django.urls import path
from accounts import views
from rest_framework_simplejwt.views import TokenRefreshView
from rest_framework_simplejwt.views import TokenObtainPairView
from rest_framework_simplejwt.views import TokenVerifyView

app_name = 'accounts'
urlpatterns = [
path('signup/', views.SignUpAPIView.as_view(), name='signup'),
path('login/', views.LoginAPIView.as_view(), name='login'),
path('logout/', views.LogoutAPIView.as_view(), name='logout'),
path('token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
path('token/verify/', TokenVerifyView.as_view(), name='token_verify'),
]
44 changes: 44 additions & 0 deletions accounts/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
from django.conf import settings
from rest_framework import generics, status, permissions
from rest_framework.response import Response

from .serializers import SignUpSerializer, LoginSerializer, LogoutSerializer

class SignUpAPIView(generics.GenericAPIView):
authentication_classes = []
serializer_class = SignUpSerializer
def post(self, request):
user = request.data
serializer = self.serializer_class(data=user)
serializer.is_valid(raise_exception=True)
serializer.save()
data = { "msg": "user created" }
return Response(data, status=status.HTTP_201_CREATED)

class LoginAPIView(generics.GenericAPIView):
authentication_classes = []
serializer_class = LoginSerializer
def post(self, request):
serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True)
access_token = serializer.data["tokens"]["access"]
refresh_token = serializer.data["tokens"]["refresh"]
data = { "msg" : "login success", "username": serializer.data["username"] }
res = Response(data, status=status.HTTP_200_OK)
res.set_cookie('access_token', value=access_token, httponly=True)
res.set_cookie('refresh_token', value=refresh_token, httponly=True)
return res

class LogoutAPIView(generics.GenericAPIView):
serializer_class = LogoutSerializer
permission_classes = [permissions.IsAuthenticated]
def post(self, request):
refresh_token = { "refresh" : request.COOKIES.get(settings.SIMPLE_JWT['REFRESH_TOKEN'])}
serializer = self.serializer_class(data = refresh_token)
serializer.is_valid(raise_exception=True)
serializer.save()
data = { "msg": "logout success" }
res = Response(data, status=status.HTTP_200_OK)
res.delete_cookie('access_token')
res.delete_cookie('refresh_token')
return res
59 changes: 58 additions & 1 deletion blending_back/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,16 @@
https://docs.djangoproject.com/en/4.0/ref/settings/
"""

from datetime import timedelta
from pathlib import Path
import os
import environ
from dotenv import load_dotenv
load_dotenv()

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent

load_dotenv()
env = environ.Env()
environ.Env.read_env(
env_file=os.path.join(BASE_DIR, '.env')
Expand All @@ -42,9 +44,39 @@
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'corsheaders',
'todos',
'accounts',
'rest_framework',
'rest_framework_simplejwt',
'rest_framework_simplejwt.token_blacklist',
]

REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'accounts.authenticate.CustomAuthentication',
),
}

REST_USE_JWT = True

SIMPLE_JWT = {
# 'ACCESS_TOKEN_LIFETIME': timedelta(minutes = 1),
'ACCESS_TOKEN_LIFETIME': timedelta(hours = 2),
'REFRESH_TOKEN_LIFETIME': timedelta(days = 7),
'ROTATE_REFRESH_TOKENS': False,
'BLACKLIST_AFTER_ROTATION': True,
'TOKEN_USER_CLASS': 'accounts.User',
'AUTH_HEADER_TYPES': 'JWT',
'USER_AUTHENTICATION_RULE': 'rest_framework_simplejwt.authentication.default_user_authentication_rule',

'ACCESS_TOKEN': 'access_token',
'REFRESH_TOKEN': 'refresh_token',

}

MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
Expand Down Expand Up @@ -85,6 +117,7 @@
}
}

AUTH_USER_MODEL = 'accounts.User'

# Password validation
# https://docs.djangoproject.com/en/4.0/ref/settings/#auth-password-validators
Expand Down Expand Up @@ -126,3 +159,27 @@
# https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

CORS_ORIGIN_ALLOW_ALL = True
CORS_ALLOW_CREDENTIALS = True

CORS_ALLOW_METHODS = (
'DELETE',
'GET',
'OPTIONS',
'PATCH',
'POST',
'PUT',
)

CORS_ALLOW_HEADERS = (
'accept',
'accept-encoding',
'authorization',
'content-type',
'dnt',
'origin',
'user-agent',
'x-csrftoken',
'x-requested-with',
)
4 changes: 3 additions & 1 deletion blending_back/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,6 @@

urlpatterns = [
path('admin/', admin.site.urls),
]
path('api/todos/', include('todos.urls')),
path('api/accounts/', include('accounts.urls'))
]
3 changes: 3 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
asgiref==3.5.2
Django==4.0.6
django-cors-headers==3.13.0
django-environ==0.9.0
environ==1.0
python-dotenv==0.20.0
sqlparse==0.4.2
djangorestframework==3.13.1
djangorestframework-simplejwt==5.0.0
3 changes: 0 additions & 3 deletions todolist/admin.py

This file was deleted.

3 changes: 0 additions & 3 deletions todolist/views.py

This file was deleted.

File renamed without changes.
4 changes: 4 additions & 0 deletions todos/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from django.contrib import admin
from todos.models import Todo

admin.site.register(Todo)
4 changes: 2 additions & 2 deletions account/apps.py → todos/apps.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from django.apps import AppConfig


class AccountConfig(AppConfig):
class TodosConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'account'
name = 'todos'
Loading