From cd3c24ab5380d6557941d43b4c71709fc3e48669 Mon Sep 17 00:00:00 2001 From: Sota Okuno Date: Thu, 18 Jul 2024 22:25:05 +0900 Subject: [PATCH 1/8] =?UTF-8?q?chore:=20requirements=E3=81=ABruff=E3=82=92?= =?UTF-8?q?=E8=BF=BD=E8=A8=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .devcontainer/requirements/Django/requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/.devcontainer/requirements/Django/requirements.txt b/.devcontainer/requirements/Django/requirements.txt index 5fddece3..f5b8eb62 100644 --- a/.devcontainer/requirements/Django/requirements.txt +++ b/.devcontainer/requirements/Django/requirements.txt @@ -17,3 +17,4 @@ uvicorn==0.30.1 channels==4.1.0 channels_redis==4.2.0 websockets==12.0.0 +ruff=0.5.2 From af89c9f0bf9ed6d3d724f9d47da447039b7e0bc6 Mon Sep 17 00:00:00 2001 From: Sota Okuno Date: Thu, 18 Jul 2024 23:15:51 +0900 Subject: [PATCH 2/8] =?UTF-8?q?feat:=20workflows/ruff.yml=E3=82=92?= =?UTF-8?q?=E4=BD=9C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ruff.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .github/workflows/ruff.yml diff --git a/.github/workflows/ruff.yml b/.github/workflows/ruff.yml new file mode 100644 index 00000000..d9768941 --- /dev/null +++ b/.github/workflows/ruff.yml @@ -0,0 +1,10 @@ +name: ruff +on: push +jobs: + ruff: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: chartboost/ruff-action@v1 + with: + args: 'format --check' \ No newline at end of file From ef84bad49b2167b9512d63c6f1139f66225d2e37 Mon Sep 17 00:00:00 2001 From: Sota Okuno Date: Sat, 20 Jul 2024 13:35:34 +0900 Subject: [PATCH 3/8] =?UTF-8?q?fix:=20=E6=A7=8B=E6=96=87=E3=82=A8=E3=83=A9?= =?UTF-8?q?=E3=83=BC=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .devcontainer/requirements/Django/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.devcontainer/requirements/Django/requirements.txt b/.devcontainer/requirements/Django/requirements.txt index f5b8eb62..e1a419c2 100644 --- a/.devcontainer/requirements/Django/requirements.txt +++ b/.devcontainer/requirements/Django/requirements.txt @@ -17,4 +17,4 @@ uvicorn==0.30.1 channels==4.1.0 channels_redis==4.2.0 websockets==12.0.0 -ruff=0.5.2 +ruff==0.5.2 From f161bdb06d89ae5afd6756fc3474952552ff79e5 Mon Sep 17 00:00:00 2001 From: Sota Okuno Date: Sat, 20 Jul 2024 16:12:00 +0900 Subject: [PATCH 4/8] =?UTF-8?q?refactor:=20formatter=E3=81=8B=E3=81=91?= =?UTF-8?q?=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pong/config/asgi.py | 31 +- pong/config/consumers.py | 3 +- pong/config/settings.py | 129 ++++---- pong/config/urls.py | 7 +- pong/config/wsgi.py | 2 +- pong/manage.py | 5 +- pong/pong/admin.py | 2 - pong/pong/apps.py | 4 +- pong/pong/middleware/auth.py | 119 +++---- pong/pong/migrations/0001_initial.py | 65 +++- pong/pong/migrations/0002_user_icon.py | 14 +- pong/pong/models.py | 94 +++--- pong/pong/urls.py | 21 +- pong/pong/utils/create_response.py | 45 ++- pong/pong/views/auth.py | 246 +++++++------- pong/pong/views/index.py | 4 +- pong/pong/views/oauth.py | 160 ++++----- pong/pong/views/test_auth.py | 441 ++++++++++++++----------- pong/pong/views/test_users.py | 88 +++-- pong/pong/views/users.py | 43 ++- pong/ruff.toml | 2 + 21 files changed, 828 insertions(+), 697 deletions(-) create mode 100644 pong/ruff.toml diff --git a/pong/config/asgi.py b/pong/config/asgi.py index 3c3b162b..2abfc98b 100644 --- a/pong/config/asgi.py +++ b/pong/config/asgi.py @@ -9,27 +9,30 @@ import os -from channels.auth import AuthMiddlewareStack from channels.routing import ProtocolTypeRouter, URLRouter from channels.security.websocket import AllowedHostsOriginValidator from django.urls import path from django.core.asgi import get_asgi_application -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings') +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings") django_asgi_app = application = get_asgi_application() # import consumers here -from .consumers import SampleConsumer +from .consumers import SampleConsumer from pong.middleware.auth import ChannelsJWTAuthenticationMiddleware -application = ProtocolTypeRouter({ - "http": django_asgi_app, - "websocket": AllowedHostsOriginValidator( - ChannelsJWTAuthenticationMiddleware( - URLRouter([ - path("test/", SampleConsumer.as_asgi()), - #path("chat/", PublicChatConsumer.as_asgi()), - ]) - ) - ), -}) +application = ProtocolTypeRouter( + { + "http": django_asgi_app, + "websocket": AllowedHostsOriginValidator( + ChannelsJWTAuthenticationMiddleware( + URLRouter( + [ + path("test/", SampleConsumer.as_asgi()), + # path("chat/", PublicChatConsumer.as_asgi()), + ] + ) + ) + ), + } +) diff --git a/pong/config/consumers.py b/pong/config/consumers.py index fceb42a1..ee0cb39c 100644 --- a/pong/config/consumers.py +++ b/pong/config/consumers.py @@ -1,8 +1,8 @@ from channels.generic.websocket import AsyncWebsocketConsumer from django.contrib.auth.models import AnonymousUser -class SampleConsumer(AsyncWebsocketConsumer): +class SampleConsumer(AsyncWebsocketConsumer): async def connect(self): if self.scope["user"] == AnonymousUser(): await self.close() @@ -15,4 +15,3 @@ async def connect(self): async def receive(self, text_data=None, bytes_data=None): print(text_data) - diff --git a/pong/config/settings.py b/pong/config/settings.py index a23101d9..5fdfcbdf 100644 --- a/pong/config/settings.py +++ b/pong/config/settings.py @@ -13,7 +13,6 @@ from pathlib import Path import datetime import os -from pathlib import Path # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = Path(__file__).resolve().parent.parent @@ -23,69 +22,69 @@ # See https://docs.djangoproject.com/en/5.0/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = 'django-insecure-f9o0g5c=(hz*lj*=qppxb4$+l-#2#g)+lr2#2f-#m*8fiskm^#' +SECRET_KEY = "django-insecure-f9o0g5c=(hz*lj*=qppxb4$+l-#2#g)+lr2#2f-#m*8fiskm^#" # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True -ALLOWED_HOSTS = ['*'] +ALLOWED_HOSTS = ["*"] # Application definition INSTALLED_APPS = [ - 'django.contrib.admin', - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', - 'pong', + "django.contrib.admin", + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", + "pong", ] MIDDLEWARE = [ - 'django.middleware.security.SecurityMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', - 'pong.middleware.auth.JWTAuthenticationMiddleware' + "django.middleware.security.SecurityMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.common.CommonMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", + "pong.middleware.auth.JWTAuthenticationMiddleware", ] -ROOT_URLCONF = 'config.urls' +ROOT_URLCONF = "config.urls" TEMPLATES = [ { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [], - 'APP_DIRS': True, - 'OPTIONS': { - 'context_processors': [ - 'django.template.context_processors.debug', - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": [], + "APP_DIRS": True, + "OPTIONS": { + "context_processors": [ + "django.template.context_processors.debug", + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", ], }, }, ] -WSGI_APPLICATION = 'config.wsgi.application' +WSGI_APPLICATION = "config.wsgi.application" # Database # https://docs.djangoproject.com/en/5.0/ref/settings/#databases -import os; + DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.postgresql_psycopg2', - 'NAME': os.getenv("POSTGRES_NAME"), - 'USER': os.getenv("POSTGRES_USER"), - 'PASSWORD': os.getenv("POSTGRES_PASSWORD"), - 'HOST': 'db', - 'PORT': 5432 + "default": { + "ENGINE": "django.db.backends.postgresql_psycopg2", + "NAME": os.getenv("POSTGRES_NAME"), + "USER": os.getenv("POSTGRES_USER"), + "PASSWORD": os.getenv("POSTGRES_PASSWORD"), + "HOST": "db", + "PORT": 5432, } } @@ -95,16 +94,16 @@ AUTH_PASSWORD_VALIDATORS = [ { - 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", }, { - 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", }, { - 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", }, { - 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", }, ] @@ -112,9 +111,9 @@ # Internationalization # https://docs.djangoproject.com/en/5.0/topics/i18n/ -LANGUAGE_CODE = 'en-us' +LANGUAGE_CODE = "en-us" -TIME_ZONE = 'UTC' +TIME_ZONE = "UTC" USE_I18N = True @@ -124,44 +123,44 @@ # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/5.0/howto/static-files/ -STATIC_URL = 'static/' -STATIC_ROOT = 'static/' +STATIC_URL = "static/" +STATIC_ROOT = "static/" # Default primary key field type # https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field -DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' +DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" # 追加 -AUTH_USER_MODEL = 'pong.User' -PRIVATE_KEY_PATH = os.path.join(BASE_DIR, 'keys', 'private_key.pem') -PUBLIC_KEY_PATH = os.path.join(BASE_DIR, 'keys', 'public_key.pem') -with open(PRIVATE_KEY_PATH, 'r') as f: +AUTH_USER_MODEL = "pong.User" +PRIVATE_KEY_PATH = os.path.join(BASE_DIR, "keys", "private_key.pem") +PUBLIC_KEY_PATH = os.path.join(BASE_DIR, "keys", "public_key.pem") +with open(PRIVATE_KEY_PATH, "r") as f: PRIVATE_KEY = f.read() -with open(PUBLIC_KEY_PATH, 'r') as f: +with open(PUBLIC_KEY_PATH, "r") as f: PUBLIC_KEY = f.read() JWT_AUTH = { - 'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1), - 'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(days=30), - 'JWT_PRIVATE_KEY': PRIVATE_KEY, - 'JWT_PUBLIC_KEY': PUBLIC_KEY, - 'JWT_ALGORITHM': 'RS256', + "JWT_EXPIRATION_DELTA": datetime.timedelta(days=1), + "JWT_REFRESH_EXPIRATION_DELTA": datetime.timedelta(days=30), + "JWT_PRIVATE_KEY": PRIVATE_KEY, + "JWT_PUBLIC_KEY": PUBLIC_KEY, + "JWT_ALGORITHM": "RS256", } -MEDIA_ROOT = os.path.join(BASE_DIR, 'media') -MEDIA_URL = '/media/' +MEDIA_ROOT = os.path.join(BASE_DIR, "media") +MEDIA_URL = "/media/" -PEPPER = os.getenv('PEPPER') -OAUTH_CALLBACK_42API = os.getenv('OAUTH_CALLBACK_42API') -CLIENT_ID_42API = os.getenv('CLIENT_ID_42API') -CLIENT_SECRET_42API = os.getenv('CLIENT_SECRET_42API') +PEPPER = os.getenv("PEPPER") +OAUTH_CALLBACK_42API = os.getenv("OAUTH_CALLBACK_42API") +CLIENT_ID_42API = os.getenv("CLIENT_ID_42API") +CLIENT_SECRET_42API = os.getenv("CLIENT_SECRET_42API") -REDIS_HOST = 'redis' +REDIS_HOST = "redis" REDIS_PORT = 6379 REDIS_DB = 0 -REDIS_URL = f'redis://{REDIS_HOST}:{REDIS_PORT}/{REDIS_DB}' +REDIS_URL = f"redis://{REDIS_HOST}:{REDIS_PORT}/{REDIS_DB}" -# channelsのためのlayerの追加  +# channelsのためのlayerの追加 CHANNEL_LAYERS = { "default": { "BACKEND": "channels_redis.core.RedisChannelLayer", diff --git a/pong/config/urls.py b/pong/config/urls.py index ab3d4540..fd25d242 100644 --- a/pong/config/urls.py +++ b/pong/config/urls.py @@ -14,12 +14,13 @@ 1. Import the include() function: from django.urls import include, path 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ + from django.contrib import admin from django.urls import path, re_path, include from pong.views.index import index urlpatterns = [ - path('admin/', admin.site.urls), - path('pong/', include('pong.urls')), - re_path(r'^.*$', index, name='index'), + path("admin/", admin.site.urls), + path("pong/", include("pong.urls")), + re_path(r"^.*$", index, name="index"), ] diff --git a/pong/config/wsgi.py b/pong/config/wsgi.py index 5c9778b1..246e0b1f 100644 --- a/pong/config/wsgi.py +++ b/pong/config/wsgi.py @@ -11,6 +11,6 @@ from django.core.wsgi import get_wsgi_application -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings') +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings") application = get_wsgi_application() diff --git a/pong/manage.py b/pong/manage.py index 8e7ac79b..aabb8181 100755 --- a/pong/manage.py +++ b/pong/manage.py @@ -1,12 +1,13 @@ #!/usr/bin/env python """Django's command-line utility for administrative tasks.""" + import os import sys def main(): """Run administrative tasks.""" - os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings') + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings") try: from django.core.management import execute_from_command_line except ImportError as exc: @@ -18,5 +19,5 @@ def main(): execute_from_command_line(sys.argv) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/pong/pong/admin.py b/pong/pong/admin.py index 8c38f3f3..846f6b40 100644 --- a/pong/pong/admin.py +++ b/pong/pong/admin.py @@ -1,3 +1 @@ -from django.contrib import admin - # Register your models here. diff --git a/pong/pong/apps.py b/pong/pong/apps.py index 40b2c396..e1771223 100644 --- a/pong/pong/apps.py +++ b/pong/pong/apps.py @@ -2,5 +2,5 @@ class PongConfig(AppConfig): - default_auto_field = 'django.db.models.BigAutoField' - name = 'pong' + default_auto_field = "django.db.models.BigAutoField" + name = "pong" diff --git a/pong/pong/middleware/auth.py b/pong/pong/middleware/auth.py index 28ed55f4..839564c7 100644 --- a/pong/pong/middleware/auth.py +++ b/pong/pong/middleware/auth.py @@ -5,89 +5,94 @@ import re from pong.models import User from pong.utils.redis_client import redis_client -from datetime import datetime, timedelta from functools import wraps from channels.db import database_sync_to_async def jwt_exempt(view_func): - @wraps(view_func) - def _wrapped_view_func(request, *args, **kwargs): - return view_func(request, *args, **kwargs) - _wrapped_view_func.jwt_exempt = True - return _wrapped_view_func + @wraps(view_func) + def _wrapped_view_func(request, *args, **kwargs): + return view_func(request, *args, **kwargs) + + _wrapped_view_func.jwt_exempt = True + return _wrapped_view_func + def getJwtPayload(token): - if not token: - return None - try: - payload = jwt.decode(token, settings.JWT_AUTH['JWT_PUBLIC_KEY'], algorithms=[settings.JWT_AUTH['JWT_ALGORITHM']]) - except jwt.ExpiredSignatureError or jwt.InvalidTokenError: - return None - return payload + if not token: + return None + try: + payload = jwt.decode( + token, + settings.JWT_AUTH["JWT_PUBLIC_KEY"], + algorithms=[settings.JWT_AUTH["JWT_ALGORITHM"]], + ) + except jwt.ExpiredSignatureError or jwt.InvalidTokenError: + return None + return payload + def getUserByJwt(token): - payload = getJwtPayload(token) - if not payload: - return None - user = User.objects.filter(uuid=payload['uuid']).first() - if not user: - return None - return user + payload = getJwtPayload(token) + if not payload: + return None + user = User.objects.filter(uuid=payload["uuid"]).first() + if not user: + return None + return user + def getJwtPayloadCookie(request): - token = request.COOKIES.get('token', None) - if not token: - return None - payload = getJwtPayload(token) - if not payload: - return None - return payload + token = request.COOKIES.get("token", None) + if not token: + return None + payload = getJwtPayload(token) + if not payload: + return None + return payload + class JWTAuthenticationMiddleware: + def __init__(self, get_response): + self.get_response = get_response - def __init__(self, get_response): - self.get_response = get_response - - def __call__(self, request): - return self.get_response(request) - - def process_view(self, request, view_func, view_args, view_kwargs): - if getattr(view_func, 'jwt_exempt', False): - return None - - if not getJwtPayloadCookie(request): - return JsonResponse({ - 'message': 'unauthorized', - 'status': 'unauthorized' - }, status=401) - token = request.COOKIES.get('token') - if redis_client.exists(token): - return JsonResponse({ - 'message': 'unauthorized', - 'status': 'unauthorized' - }, status=401) - return None + def __call__(self, request): + return self.get_response(request) -class ChannelsJWTAuthenticationMiddleware: + def process_view(self, request, view_func, view_args, view_kwargs): + if getattr(view_func, "jwt_exempt", False): + return None + + if not getJwtPayloadCookie(request): + return JsonResponse( + {"message": "unauthorized", "status": "unauthorized"}, status=401 + ) + token = request.COOKIES.get("token") + if redis_client.exists(token): + return JsonResponse( + {"message": "unauthorized", "status": "unauthorized"}, status=401 + ) + return None + +class ChannelsJWTAuthenticationMiddleware: def __init__(self, app): self.app = app async def __call__(self, scope, receive, send): - headers = dict(scope['headers']) + headers = dict(scope["headers"]) try: - match = re.search(r'token=([^\s;]+)', headers[b'cookie'].decode('utf-8')) + match = re.search(r"token=([^\s;]+)", headers[b"cookie"].decode("utf-8")) if not match: - scope['user'] = AnonymousUser() + scope["user"] = AnonymousUser() return await self.app(scope, receive, send) token = match.group(1) - scope['user'] = await database_sync_to_async(getUserByJwt)(token) - if scope['user'] == None: - scope['user'] = AnonymousUser() + scope["user"] = await database_sync_to_async(getUserByJwt)(token) + if scope["user"] is None: + scope["user"] = AnonymousUser() return await self.app(scope, receive, send) return await self.app(scope, receive, send) except Exception as e: print(f"Exception: {e}") - scope['user'] = AnonymousUser() + scope["user"] = AnonymousUser() return await self.app(scope, receive, send) diff --git a/pong/pong/migrations/0001_initial.py b/pong/pong/migrations/0001_initial.py index 746f2880..49849b77 100644 --- a/pong/pong/migrations/0001_initial.py +++ b/pong/pong/migrations/0001_initial.py @@ -5,29 +5,68 @@ class Migration(migrations.Migration): - initial = True dependencies = [ - ('auth', '0012_alter_user_first_name_max_length'), + ("auth", "0012_alter_user_first_name_max_length"), ] operations = [ migrations.CreateModel( - name='User', + name="User", fields=[ - ('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')), - ('uuid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), - ('name', models.CharField(max_length=20, unique=True)), - ('email', models.EmailField(max_length=254, unique=True)), - ('is_staff', models.BooleanField(default=False)), - ('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')), + ("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", + ), + ), + ( + "uuid", + models.UUIDField( + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + ), + ), + ("name", models.CharField(max_length=20, unique=True)), + ("email", models.EmailField(max_length=254, unique=True)), + ("is_staff", models.BooleanField(default=False)), + ( + "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={ - 'db_table': 'users', + "db_table": "users", }, ), ] diff --git a/pong/pong/migrations/0002_user_icon.py b/pong/pong/migrations/0002_user_icon.py index 3a8d1ef5..ee30044a 100644 --- a/pong/pong/migrations/0002_user_icon.py +++ b/pong/pong/migrations/0002_user_icon.py @@ -4,15 +4,19 @@ class Migration(migrations.Migration): - dependencies = [ - ('pong', '0001_initial'), + ("pong", "0001_initial"), ] operations = [ migrations.AddField( - model_name='user', - name='icon', - field=models.ImageField(blank=True, default='defaults/default.jpeg', null=True, upload_to='uploads'), + model_name="user", + name="icon", + field=models.ImageField( + blank=True, + default="defaults/default.jpeg", + null=True, + upload_to="uploads", + ), ), ] diff --git a/pong/pong/models.py b/pong/pong/models.py index 140b5e42..23a7a48d 100644 --- a/pong/pong/models.py +++ b/pong/pong/models.py @@ -1,53 +1,57 @@ from django.db import models import uuid -from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin, BaseUserManager +from django.contrib.auth.models import ( + AbstractBaseUser, + PermissionsMixin, + BaseUserManager, +) + class UserManager(BaseUserManager): - def create_user(self, name, email, password, icon=None, **extra_fields): - if not name: - raise ValueError('UserIDを入力してください') - if not email: - raise ValueError('メールアドレスを入力してください') - if not password: - raise ValueError('パスワードを入力してください') - - email = self.normalize_email(email) - user = self.model( - name = name, - email = email, - **extra_fields - ) - if icon: - user.icon = icon - - user.set_password(password) - user.save(using=self._db) - return user - - def create_superuser(self, name, email, password, **extra_fields): - extra_fields.setdefault('is_staff', True) - extra_fields.setdefault('is_superuser', True) - - if extra_fields.get('is_staff') is not True: - raise ValueError(_('Superuser must have is_staff=True.')) - if extra_fields.get('is_superuser') is not True: - raise ValueError(_('Superuser must have is_superuser=True.')) - - return self.create_user(name, email, password, **extra_fields) + def create_user(self, name, email, password, icon=None, **extra_fields): + if not name: + raise ValueError("UserIDを入力してください") + if not email: + raise ValueError("メールアドレスを入力してください") + if not password: + raise ValueError("パスワードを入力してください") -class User(AbstractBaseUser, PermissionsMixin): - uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) - name = models.CharField(unique=True, blank=False, max_length=20) - email = models.EmailField(unique=True, blank=False) - is_staff = models.BooleanField(default=False) - icon = models.ImageField(upload_to='uploads', blank=True, null=True, default='defaults/default.jpeg') - objects = UserManager() + email = self.normalize_email(email) + user = self.model(name=name, email=email, **extra_fields) + if icon: + user.icon = icon + + user.set_password(password) + user.save(using=self._db) + return user - USERNAME_FIELD = 'name' - REQUIRED_FIELDS = ['email'] + def create_superuser(self, name, email, password, **extra_fields): + extra_fields.setdefault("is_staff", True) + extra_fields.setdefault("is_superuser", True) - def __str__(self): - return self.name + if extra_fields.get("is_staff") is not True: + raise ValueError("Superuser must have is_staff=True.") + if extra_fields.get("is_superuser") is not True: + raise ValueError("Superuser must have is_superuser=True.") - class Meta: - db_table = 'users' + return self.create_user(name, email, password, **extra_fields) + + +class User(AbstractBaseUser, PermissionsMixin): + uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) + name = models.CharField(unique=True, blank=False, max_length=20) + email = models.EmailField(unique=True, blank=False) + is_staff = models.BooleanField(default=False) + icon = models.ImageField( + upload_to="uploads", blank=True, null=True, default="defaults/default.jpeg" + ) + objects = UserManager() + + USERNAME_FIELD = "name" + REQUIRED_FIELDS = ["email"] + + def __str__(self): + return self.name + + class Meta: + db_table = "users" diff --git a/pong/pong/urls.py b/pong/pong/urls.py index c5261c4d..15bbf26b 100644 --- a/pong/pong/urls.py +++ b/pong/pong/urls.py @@ -2,18 +2,17 @@ from .views import users from django.conf import settings from django.conf.urls.static import static -import uuid from .views import auth, oauth -app_name = 'pong' +app_name = "pong" urlpatterns = [ - path('api/v1/auth/register', auth.register, name='register'), - path('api/v1/auth/token', auth.create_token, name='token'), - path('api/v1/auth/token/refresh', auth.refresh_token, name='refresh'), - path('api/v1/auth/token/verify', auth.verify_token, name='verify'), - path('api/v1/users/', users.get_user, name='get_user'), - path('api/v1/auth/token/revoke', auth.revoke_token, name='revoke'), - path('oauth/42/signup', oauth.oauth_42_signup, name='oauth42_signup'), - path('oauth/42/signin', oauth.oauth_42_signin, name='oauth42_signin'), - path('oauth/callback/42', oauth.callback_42, name='callback42'), + path("api/v1/auth/register", auth.register, name="register"), + path("api/v1/auth/token", auth.create_token, name="token"), + path("api/v1/auth/token/refresh", auth.refresh_token, name="refresh"), + path("api/v1/auth/token/verify", auth.verify_token, name="verify"), + path("api/v1/users/", users.get_user, name="get_user"), + path("api/v1/auth/token/revoke", auth.revoke_token, name="revoke"), + path("oauth/42/signup", oauth.oauth_42_signup, name="oauth42_signup"), + path("oauth/42/signin", oauth.oauth_42_signin, name="oauth42_signin"), + path("oauth/callback/42", oauth.callback_42, name="callback42"), ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) diff --git a/pong/pong/utils/create_response.py b/pong/pong/utils/create_response.py index 9bfa8e51..a249e0d8 100644 --- a/pong/pong/utils/create_response.py +++ b/pong/pong/utils/create_response.py @@ -2,25 +2,34 @@ from datetime import datetime import jwt + def create_token_response(uuid, response): - new_payload = { - 'uuid': str(uuid), - 'exp': datetime.utcnow() + settings.JWT_AUTH['JWT_EXPIRATION_DELTA'], - 'iat': datetime.utcnow() - } - new_token = jwt.encode(new_payload, settings.JWT_AUTH['JWT_PRIVATE_KEY'], algorithm=settings.JWT_AUTH['JWT_ALGORITHM']) + new_payload = { + "uuid": str(uuid), + "exp": datetime.utcnow() + settings.JWT_AUTH["JWT_EXPIRATION_DELTA"], + "iat": datetime.utcnow(), + } + new_token = jwt.encode( + new_payload, + settings.JWT_AUTH["JWT_PRIVATE_KEY"], + algorithm=settings.JWT_AUTH["JWT_ALGORITHM"], + ) - new_refresh_payload = { - 'uuid': str(uuid), - 'exp': datetime.utcnow() + settings.JWT_AUTH['JWT_REFRESH_EXPIRATION_DELTA'], - 'iat': datetime.utcnow() - } - new_refresh_token = jwt.encode(new_refresh_payload, settings.JWT_AUTH['JWT_PRIVATE_KEY'], algorithm=settings.JWT_AUTH['JWT_ALGORITHM']) + new_refresh_payload = { + "uuid": str(uuid), + "exp": datetime.utcnow() + settings.JWT_AUTH["JWT_REFRESH_EXPIRATION_DELTA"], + "iat": datetime.utcnow(), + } + new_refresh_token = jwt.encode( + new_refresh_payload, + settings.JWT_AUTH["JWT_PRIVATE_KEY"], + algorithm=settings.JWT_AUTH["JWT_ALGORITHM"], + ) - # HTTPS実装後に有効化する - # response.set_cookie('token', new_token, httponly=True, secure=True) - # response.set_cookie('refresh_token', new_refresh_token, httponly=True, secure=True) - response.set_cookie('token', new_token, httponly=True) - response.set_cookie('refresh_token', new_refresh_token, httponly=True) + # HTTPS実装後に有効化する + # response.set_cookie('token', new_token, httponly=True, secure=True) + # response.set_cookie('refresh_token', new_refresh_token, httponly=True, secure=True) + response.set_cookie("token", new_token, httponly=True) + response.set_cookie("refresh_token", new_refresh_token, httponly=True) - return response + return response diff --git a/pong/pong/views/auth.py b/pong/pong/views/auth.py index c6198fe8..006901cc 100644 --- a/pong/pong/views/auth.py +++ b/pong/pong/views/auth.py @@ -1,164 +1,156 @@ import json from django.http.response import JsonResponse -from pong.models import UserManager from pong.models import User from django.views.decorators.csrf import csrf_exempt -from datetime import datetime, timedelta +from datetime import datetime from django.conf import settings -from django.http.response import HttpResponse from pong.middleware.auth import jwt_exempt, getJwtPayloadCookie from pong.utils.create_response import create_token_response from pong.utils.redis_client import redis_client -import requests import jwt -import base64 -import os @jwt_exempt @csrf_exempt def test(request): - return JsonResponse({ - 'message': 'Hello, world!' - }) + return JsonResponse({"message": "Hello, world!"}) + @jwt_exempt @csrf_exempt def register(request): - if request.method != 'POST': - return JsonResponse({ - 'message': 'Method is not allowed', - 'status': 'invalidParams' - }, status=400) - - name = request.POST.get('name') - email = request.POST.get('email') - password = request.POST.get('password') - icon = request.FILES.get('icon', None) - - if not all([name, email, password]): - return JsonResponse({ - 'message': 'Invalid parameters', - 'status': 'invalidParams' - }, status=400) - - if User.objects.filter(name=name).exists() or User.objects.filter(email=email).exists(): - return JsonResponse({ - 'message': 'User already exists', - 'status': 'registerConflict' - }, status=409) - - user = User.objects.create_user(name=name, email=email, password=password, icon=icon) - - return JsonResponse({ - 'uuid': str(user.uuid) - }, status=201) + if request.method != "POST": + return JsonResponse( + {"message": "Method is not allowed", "status": "invalidParams"}, status=400 + ) + + name = request.POST.get("name") + email = request.POST.get("email") + password = request.POST.get("password") + icon = request.FILES.get("icon", None) + + if not all([name, email, password]): + return JsonResponse( + {"message": "Invalid parameters", "status": "invalidParams"}, status=400 + ) + + if ( + User.objects.filter(name=name).exists() + or User.objects.filter(email=email).exists() + ): + return JsonResponse( + {"message": "User already exists", "status": "registerConflict"}, status=409 + ) + + user = User.objects.create_user( + name=name, email=email, password=password, icon=icon + ) + + return JsonResponse({"uuid": str(user.uuid)}, status=201) + @jwt_exempt @csrf_exempt def create_token(request): - if request.method != 'POST': - return JsonResponse({ - 'message': 'Method is not allowed', - 'status': 'invalidParams' - }, status=400) + if request.method != "POST": + return JsonResponse( + {"message": "Method is not allowed", "status": "invalidParams"}, status=400 + ) + + data = json.loads(request.body) - data = json.loads(request.body) + if "email" not in data or "password" not in data: + return JsonResponse( + {"message": "Invalid parameters", "status": "invalidParams"}, status=400 + ) - if 'email' not in data or 'password' not in data: - return JsonResponse({ - 'message': 'Invalid parameters', - 'status': 'invalidParams' - }, status=400) + user = User.objects.filter(email=data["email"]).first() - user = User.objects.filter(email=data['email']).first() + if not user or not user.check_password(data["password"]): + return JsonResponse( + {"message": "User not found", "status": "userNotFound"}, status=404 + ) - if not user or not user.check_password(data['password']): - return JsonResponse({ - 'message': 'User not found', - 'status': 'userNotFound' - }, status=404) + return create_token_response( + user.uuid, JsonResponse({"uuid": user.uuid}, content_type="application/json") + ) - return create_token_response(user.uuid, JsonResponse({'uuid': user.uuid}, content_type='application/json')) @jwt_exempt @csrf_exempt def refresh_token(request): - if request.method != 'POST': - return JsonResponse({ - 'message': 'Method is not allowed', - 'status': 'invalidParams' - }, status=400) - - refresh_token = request.COOKIES.get('refresh_token') - if not refresh_token: - return JsonResponse({ - 'message': 'Refresh token not provided', - 'status': 'invalidParams' - }, status=400) - - try: - refresh_payload = jwt.decode(refresh_token, settings.JWT_AUTH['JWT_PUBLIC_KEY'], algorithms=[settings.JWT_AUTH['JWT_ALGORITHM']]) - except jwt.ExpiredSignatureError: - return JsonResponse({ - 'message': 'Refresh token has expired', - 'status': 'invalidParams' - }, status=400) - except jwt.InvalidTokenError: - return JsonResponse({ - 'message': 'Invalid refresh token', - 'status': 'invalidParams' - }, status=400) - - user = User.objects.filter(uuid=refresh_payload['uuid']).first() - if not user: - return JsonResponse({ - 'message': 'User not found', - 'status': 'userNotFound' - }, status=404) - - return create_token_response(user.uuid, JsonResponse({'uuid': user.uuid}, content_type='application/json')) + if request.method != "POST": + return JsonResponse( + {"message": "Method is not allowed", "status": "invalidParams"}, status=400 + ) + + refresh_token = request.COOKIES.get("refresh_token") + if not refresh_token: + return JsonResponse( + {"message": "Refresh token not provided", "status": "invalidParams"}, + status=400, + ) + + try: + refresh_payload = jwt.decode( + refresh_token, + settings.JWT_AUTH["JWT_PUBLIC_KEY"], + algorithms=[settings.JWT_AUTH["JWT_ALGORITHM"]], + ) + except jwt.ExpiredSignatureError: + return JsonResponse( + {"message": "Refresh token has expired", "status": "invalidParams"}, + status=400, + ) + except jwt.InvalidTokenError: + return JsonResponse( + {"message": "Invalid refresh token", "status": "invalidParams"}, status=400 + ) + + user = User.objects.filter(uuid=refresh_payload["uuid"]).first() + if not user: + return JsonResponse( + {"message": "User not found", "status": "userNotFound"}, status=404 + ) + + return create_token_response( + user.uuid, JsonResponse({"uuid": user.uuid}, content_type="application/json") + ) + @csrf_exempt def verify_token(request): - if request.method != 'POST': - return JsonResponse({ - 'message': 'Method is not allowed', - 'status': 'invalidParams' - }, status=400) - - payload = getJwtPayloadCookie(request) - uuid = payload.get('uuid', None) - if not payload or not uuid: - return JsonResponse({ - 'message': 'unauthorized', - 'status': 'unauthorized' - }, status=401) - token = request.COOKIES.get('token') - if redis_client.exists(token): - return JsonResponse({ - 'message': 'unauthorized', - 'status': 'unauthorized' - }, status=401) - return JsonResponse({ - 'uuid': str(uuid) - }, status=200) + if request.method != "POST": + return JsonResponse( + {"message": "Method is not allowed", "status": "invalidParams"}, status=400 + ) + + payload = getJwtPayloadCookie(request) + uuid = payload.get("uuid", None) + if not payload or not uuid: + return JsonResponse( + {"message": "unauthorized", "status": "unauthorized"}, status=401 + ) + token = request.COOKIES.get("token") + if redis_client.exists(token): + return JsonResponse( + {"message": "unauthorized", "status": "unauthorized"}, status=401 + ) + return JsonResponse({"uuid": str(uuid)}, status=200) + @csrf_exempt def revoke_token(request): - if request.method != 'POST': - return JsonResponse({ - 'message': 'Method is not allowed', - 'status': 'invalidParams' - }, status=400) - token = request.COOKIES.get('token', None) - payload = getJwtPayloadCookie(request) - exp = payload['exp'] - uuid = payload['uuid'] - current_time = datetime.utcnow() - exp_time = datetime.utcfromtimestamp(exp) - ttl = int((exp_time - current_time).total_seconds()) - redis_client.setex(token, ttl, 'blacklisted') - return JsonResponse({ - 'uuid': str(uuid) - }, status=200) + if request.method != "POST": + return JsonResponse( + {"message": "Method is not allowed", "status": "invalidParams"}, status=400 + ) + token = request.COOKIES.get("token", None) + payload = getJwtPayloadCookie(request) + exp = payload["exp"] + uuid = payload["uuid"] + current_time = datetime.utcnow() + exp_time = datetime.utcfromtimestamp(exp) + ttl = int((exp_time - current_time).total_seconds()) + redis_client.setex(token, ttl, "blacklisted") + return JsonResponse({"uuid": str(uuid)}, status=200) diff --git a/pong/pong/views/index.py b/pong/pong/views/index.py index b3f5eace..bab781b9 100644 --- a/pong/pong/views/index.py +++ b/pong/pong/views/index.py @@ -1,9 +1,9 @@ -from django.http.response import JsonResponse from django.views.decorators.csrf import csrf_exempt from pong.middleware.auth import jwt_exempt from django.shortcuts import render + @jwt_exempt @csrf_exempt def index(request): - return render(request, 'pong/index.html') \ No newline at end of file + return render(request, "pong/index.html") diff --git a/pong/pong/views/oauth.py b/pong/pong/views/oauth.py index 63455eae..bbc6ebad 100644 --- a/pong/pong/views/oauth.py +++ b/pong/pong/views/oauth.py @@ -1,109 +1,113 @@ from django.views.decorators.csrf import csrf_exempt from django.http.response import JsonResponse, HttpResponseRedirect from django.conf import settings -from django.urls import reverse from pong.middleware.auth import jwt_exempt from pong.models import User from pong.utils.create_response import create_token_response -from datetime import datetime, timedelta from urllib.parse import urlencode, quote, unquote import requests import base64 import json -import jwt import os + def redirect_to_oauth(action): - base_url = 'https://api.intra.42.fr/oauth/authorize' - state = quote(json.dumps({'action': action})) + base_url = "https://api.intra.42.fr/oauth/authorize" + state = quote(json.dumps({"action": action})) params = { - 'client_id': settings.CLIENT_ID_42API, - 'redirect_uri': settings.OAUTH_CALLBACK_42API, - 'response_type': 'code', - 'state': state, + "client_id": settings.CLIENT_ID_42API, + "redirect_uri": settings.OAUTH_CALLBACK_42API, + "response_type": "code", + "state": state, } query_string = urlencode(params) url = f"{base_url}?{query_string}" return HttpResponseRedirect(url) + @jwt_exempt @csrf_exempt def oauth_42_signup(request): - if request.method != 'GET': - return JsonResponse({ - 'message': 'Method is not allowed', - 'status': 'invalidParams' - }, status=400) - return redirect_to_oauth(action='signup') + if request.method != "GET": + return JsonResponse( + {"message": "Method is not allowed", "status": "invalidParams"}, status=400 + ) + return redirect_to_oauth(action="signup") + @jwt_exempt @csrf_exempt def oauth_42_signin(request): - if request.method != 'GET': - return JsonResponse({ - 'message': 'Method is not allowed', - 'status': 'invalidParams' - }, status=400) - return redirect_to_oauth(action='signin') + if request.method != "GET": + return JsonResponse( + {"message": "Method is not allowed", "status": "invalidParams"}, status=400 + ) + return redirect_to_oauth(action="signin") + @jwt_exempt @csrf_exempt def callback_42(request): - state = request.GET.get('state') - if not state: - return HttpResponseRedirect(redirect_to='/#invalidParameters') - - state_data = json.loads(unquote(state)) - action = state_data.get('action', None) - - if action not in ['signup', 'signin']: - return HttpResponseRedirect(redirect_to='/#invalidParameters') - - path = 'signup' if action == 'signup' else '' - - if request.method != 'GET': - return HttpResponseRedirect(redirect_to=f'/{path}#methodNotAllowed') - - code = request.GET.get('code') - if not code: - return HttpResponseRedirect(redirect_to=f'/{path}#failedToGetCode') - - params = { - 'grant_type': 'authorization_code', - 'client_id': settings.CLIENT_ID_42API, - 'client_secret': settings.CLIENT_SECRET_42API, - 'code': code, - 'redirect_uri': settings.OAUTH_CALLBACK_42API, - } - - response_token = requests.post('https://api.intra.42.fr/oauth/token', data=params) - if response_token.status_code != 200: - return HttpResponseRedirect(redirect_to=f'/{path}#failedToGetToken') - - access_token = response_token.json().get('access_token') - headers = { - 'Authorization': f'Bearer {access_token}', - } - - response_user_info = requests.get('https://api.intra.42.fr/v2/me', headers=headers) - if response_user_info.status_code != 200: - return HttpResponseRedirect(redirect_to=f'/{path}#failedToGetUserInfo') - - data_user_info = response_user_info.json() - login = data_user_info.get('login', None) - email = data_user_info.get('email', None) - if (not login) or (not email): - return HttpResponseRedirect(redirect_to=f'/{path}#failedToGetUserInfo') - - if action == 'signup': - random_password = base64.urlsafe_b64encode(os.urandom(16)).decode('utf-8') - if User.objects.filter(name=login).exists() or User.objects.filter(email=email).exists(): - return HttpResponseRedirect(redirect_to=f'/{path}#userAlreadyExists') - user = User.objects.create_user(name=login, email=email, password=random_password) - else: # action == 'signin' - user = User.objects.filter(name=login, email=email).first() - if not user: - return HttpResponseRedirect(redirect_to='/#userDoesNotExist') - - return create_token_response(user.uuid, HttpResponseRedirect(redirect_to='/home')) + state = request.GET.get("state") + if not state: + return HttpResponseRedirect(redirect_to="/#invalidParameters") + + state_data = json.loads(unquote(state)) + action = state_data.get("action", None) + + if action not in ["signup", "signin"]: + return HttpResponseRedirect(redirect_to="/#invalidParameters") + + path = "signup" if action == "signup" else "" + + if request.method != "GET": + return HttpResponseRedirect(redirect_to=f"/{path}#methodNotAllowed") + + code = request.GET.get("code") + if not code: + return HttpResponseRedirect(redirect_to=f"/{path}#failedToGetCode") + + params = { + "grant_type": "authorization_code", + "client_id": settings.CLIENT_ID_42API, + "client_secret": settings.CLIENT_SECRET_42API, + "code": code, + "redirect_uri": settings.OAUTH_CALLBACK_42API, + } + + response_token = requests.post("https://api.intra.42.fr/oauth/token", data=params) + if response_token.status_code != 200: + return HttpResponseRedirect(redirect_to=f"/{path}#failedToGetToken") + + access_token = response_token.json().get("access_token") + headers = { + "Authorization": f"Bearer {access_token}", + } + + response_user_info = requests.get("https://api.intra.42.fr/v2/me", headers=headers) + if response_user_info.status_code != 200: + return HttpResponseRedirect(redirect_to=f"/{path}#failedToGetUserInfo") + + data_user_info = response_user_info.json() + login = data_user_info.get("login", None) + email = data_user_info.get("email", None) + if (not login) or (not email): + return HttpResponseRedirect(redirect_to=f"/{path}#failedToGetUserInfo") + + if action == "signup": + random_password = base64.urlsafe_b64encode(os.urandom(16)).decode("utf-8") + if ( + User.objects.filter(name=login).exists() + or User.objects.filter(email=email).exists() + ): + return HttpResponseRedirect(redirect_to=f"/{path}#userAlreadyExists") + user = User.objects.create_user( + name=login, email=email, password=random_password + ) + else: # action == 'signin' + user = User.objects.filter(name=login, email=email).first() + if not user: + return HttpResponseRedirect(redirect_to="/#userDoesNotExist") + + return create_token_response(user.uuid, HttpResponseRedirect(redirect_to="/home")) diff --git a/pong/pong/views/test_auth.py b/pong/pong/views/test_auth.py index 6f62690f..84fad789 100644 --- a/pong/pong/views/test_auth.py +++ b/pong/pong/views/test_auth.py @@ -3,237 +3,298 @@ from pong.models import User import jwt from django.conf import settings -from http.cookies import SimpleCookie from datetime import datetime, timedelta + class UserRegisterTest(TestCase): - def test_register_normal(self): - data = { - 'name': 'ユーザー名', - 'email': 'example@email.com', - 'password': 'p4s$W0rd' - } - response = self.client.post(reverse('pong:register'), data=data, content_type='application/json') - user = User.objects.filter(name=data['name']).first() - self.assertEqual(response.json()['uuid'], str(user.uuid)) - self.assertTrue(user.check_password(data['password'])) - self.assertEqual(response.status_code, 201) - - def test_register_not_allowed_method(self): - response = self.client.get(reverse('pong:register')) - self.assertEqual(response.json()['message'], 'Method is not allowed') - self.assertEqual(response.json()['status'], 'invalidParams') - self.assertEqual(response.status_code, 400) - - def test_register_no_name(self): - data = { - 'email': 'example@email.com', - 'password': 'p4s$W0rd' - } - response = self.client.post(reverse('pong:register'), data=data, content_type='application/json') - self.assertEqual(response.json()['message'], 'Invalid parameters') - self.assertEqual(response.json()['status'], 'invalidParams') - self.assertEqual(response.status_code, 400) - - def test_register_no_email(self): - data = { - 'name': 'ユーザー名', - 'password': 'p4s$W0rd' - } - response = self.client.post(reverse('pong:register'), data=data, content_type='application/json') - self.assertEqual(response.json()['message'], 'Invalid parameters') - self.assertEqual(response.json()['status'], 'invalidParams') - self.assertEqual(response.status_code, 400) - - def test_register_no_password(self): - data = { - 'name': 'ユーザー名', - 'email': 'example@email.com', - } - response = self.client.post(reverse('pong:register'), data=data, content_type='application/json') - self.assertEqual(response.json()['message'], 'Invalid parameters') - self.assertEqual(response.json()['status'], 'invalidParams') - self.assertEqual(response.status_code, 400) - - def test_register_user_already_exists(self): - User.objects.create_user(name='ユーザー名', email='example@email1.com', password='p4s$W0rd') - data = { - 'name': 'ユーザー名', - 'email': 'example2@email.com', - 'password': 'p4s$W0rd' - } - response = self.client.post(reverse('pong:register'), data=data, content_type='application/json') - self.assertEqual(response.json()['message'], 'User already exists') - self.assertEqual(response.json()['status'], 'registerConflict') - self.assertEqual(response.status_code, 409) - - def test_register_email_already_exists(self): - User.objects.create_user(name='ユーザー名1', email='example@email.com', password='p4s$W0rd') - data = { - 'name': 'ユーザー名2', - 'email': 'example@email.com', - 'password': 'p4s$W0rd' - } - response = self.client.post(reverse('pong:register'), data=data, content_type='application/json') - self.assertEqual(response.json()['message'], 'User already exists') - self.assertEqual(response.json()['status'], 'registerConflict') - self.assertEqual(response.status_code, 409) + def test_register_normal(self): + data = { + "name": "ユーザー名", + "email": "example@email.com", + "password": "p4s$W0rd", + } + response = self.client.post( + reverse("pong:register"), data=data, content_type="application/json" + ) + user = User.objects.filter(name=data["name"]).first() + self.assertEqual(response.json()["uuid"], str(user.uuid)) + self.assertTrue(user.check_password(data["password"])) + self.assertEqual(response.status_code, 201) + + def test_register_not_allowed_method(self): + response = self.client.get(reverse("pong:register")) + self.assertEqual(response.json()["message"], "Method is not allowed") + self.assertEqual(response.json()["status"], "invalidParams") + self.assertEqual(response.status_code, 400) + + def test_register_no_name(self): + data = {"email": "example@email.com", "password": "p4s$W0rd"} + response = self.client.post( + reverse("pong:register"), data=data, content_type="application/json" + ) + self.assertEqual(response.json()["message"], "Invalid parameters") + self.assertEqual(response.json()["status"], "invalidParams") + self.assertEqual(response.status_code, 400) + + def test_register_no_email(self): + data = {"name": "ユーザー名", "password": "p4s$W0rd"} + response = self.client.post( + reverse("pong:register"), data=data, content_type="application/json" + ) + self.assertEqual(response.json()["message"], "Invalid parameters") + self.assertEqual(response.json()["status"], "invalidParams") + self.assertEqual(response.status_code, 400) + + def test_register_no_password(self): + data = { + "name": "ユーザー名", + "email": "example@email.com", + } + response = self.client.post( + reverse("pong:register"), data=data, content_type="application/json" + ) + self.assertEqual(response.json()["message"], "Invalid parameters") + self.assertEqual(response.json()["status"], "invalidParams") + self.assertEqual(response.status_code, 400) + + def test_register_user_already_exists(self): + User.objects.create_user( + name="ユーザー名", email="example@email1.com", password="p4s$W0rd" + ) + data = { + "name": "ユーザー名", + "email": "example2@email.com", + "password": "p4s$W0rd", + } + response = self.client.post( + reverse("pong:register"), data=data, content_type="application/json" + ) + self.assertEqual(response.json()["message"], "User already exists") + self.assertEqual(response.json()["status"], "registerConflict") + self.assertEqual(response.status_code, 409) + + def test_register_email_already_exists(self): + User.objects.create_user( + name="ユーザー名1", email="example@email.com", password="p4s$W0rd" + ) + data = { + "name": "ユーザー名2", + "email": "example@email.com", + "password": "p4s$W0rd", + } + response = self.client.post( + reverse("pong:register"), data=data, content_type="application/json" + ) + self.assertEqual(response.json()["message"], "User already exists") + self.assertEqual(response.json()["status"], "registerConflict") + self.assertEqual(response.status_code, 409) + class UserTokenTest(TestCase): - def test_token_normal(self): - user = User.objects.create_user(name='ユーザー名', email='example@email.com', password='p4s$W0rd') - data = { - "email": "example@email.com", - "password": "p4s$W0rd" - } - response = self.client.post(reverse('pong:token'), data=data, content_type='application/json') - self.assertEqual(response.status_code, 200) - self.assertEqual(response.json()['uuid'], str(user.uuid)) - - token = response.client.cookies['token'].value - try: - decoded_token = jwt.decode(token, settings.JWT_AUTH['JWT_PUBLIC_KEY'], algorithms=['RS256']) - except jwt.ExpiredSignatureError: - self.fail('Token is expired') - except jwt.InvalidTokenError: - self.fail('Token is invalid') - self.assertIn('uuid', decoded_token) - self.assertIn('exp', decoded_token) - self.assertIn('iat', decoded_token) - self.assertEqual(decoded_token['uuid'], str(user.uuid)) - - refresh_token = response.client.cookies['refresh_token'].value - try: - decoded_refresh_token = jwt.decode(refresh_token, settings.JWT_AUTH['JWT_PUBLIC_KEY'], algorithms=['RS256']) - except jwt.ExpiredSignatureError: - self.fail('Refresh token is expired') - except jwt.InvalidTokenError: - self.fail('Refresh token is invalid') - self.assertIn('uuid', decoded_refresh_token) - self.assertIn('exp', decoded_refresh_token) - self.assertIn('iat', decoded_refresh_token) - self.assertEqual(decoded_refresh_token['uuid'], str(user.uuid)) - - def test_token_no_email(self): - data = { - "password": "p4s$W0rd" - } - response = self.client.post(reverse('pong:token'), data=data, content_type='application/json') - self.assertEqual(response.status_code, 400) - self.assertEqual(response.json()['message'], 'Invalid parameters') - self.assertEqual(response.json()['status'], 'invalidParams') - - def test_token_no_password(self): - data = { - "email": "example@email.com", - } - response = self.client.post(reverse('pong:token'), data=data, content_type='application/json') - self.assertEqual(response.status_code, 400) - self.assertEqual(response.json()['message'], 'Invalid parameters') - self.assertEqual(response.json()['status'], 'invalidParams') - - def test_token_user_not_found(self): - data = { - "email": "example@email.com", - "password": "p4s$W0rd" - } - response = self.client.post(reverse('pong:token'), data=data, content_type='application/json') - self.assertEqual(response.status_code, 404) - self.assertEqual(response.json()['message'], 'User not found') - self.assertEqual(response.json()['status'], 'userNotFound') + def test_token_normal(self): + user = User.objects.create_user( + name="ユーザー名", email="example@email.com", password="p4s$W0rd" + ) + data = {"email": "example@email.com", "password": "p4s$W0rd"} + response = self.client.post( + reverse("pong:token"), data=data, content_type="application/json" + ) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json()["uuid"], str(user.uuid)) + + token = response.client.cookies["token"].value + try: + decoded_token = jwt.decode( + token, settings.JWT_AUTH["JWT_PUBLIC_KEY"], algorithms=["RS256"] + ) + except jwt.ExpiredSignatureError: + self.fail("Token is expired") + except jwt.InvalidTokenError: + self.fail("Token is invalid") + self.assertIn("uuid", decoded_token) + self.assertIn("exp", decoded_token) + self.assertIn("iat", decoded_token) + self.assertEqual(decoded_token["uuid"], str(user.uuid)) + + refresh_token = response.client.cookies["refresh_token"].value + try: + decoded_refresh_token = jwt.decode( + refresh_token, settings.JWT_AUTH["JWT_PUBLIC_KEY"], algorithms=["RS256"] + ) + except jwt.ExpiredSignatureError: + self.fail("Refresh token is expired") + except jwt.InvalidTokenError: + self.fail("Refresh token is invalid") + self.assertIn("uuid", decoded_refresh_token) + self.assertIn("exp", decoded_refresh_token) + self.assertIn("iat", decoded_refresh_token) + self.assertEqual(decoded_refresh_token["uuid"], str(user.uuid)) + + def test_token_no_email(self): + data = {"password": "p4s$W0rd"} + response = self.client.post( + reverse("pong:token"), data=data, content_type="application/json" + ) + self.assertEqual(response.status_code, 400) + self.assertEqual(response.json()["message"], "Invalid parameters") + self.assertEqual(response.json()["status"], "invalidParams") + + def test_token_no_password(self): + data = { + "email": "example@email.com", + } + response = self.client.post( + reverse("pong:token"), data=data, content_type="application/json" + ) + self.assertEqual(response.status_code, 400) + self.assertEqual(response.json()["message"], "Invalid parameters") + self.assertEqual(response.json()["status"], "invalidParams") + + def test_token_user_not_found(self): + data = {"email": "example@email.com", "password": "p4s$W0rd"} + response = self.client.post( + reverse("pong:token"), data=data, content_type="application/json" + ) + self.assertEqual(response.status_code, 404) + self.assertEqual(response.json()["message"], "User not found") + self.assertEqual(response.json()["status"], "userNotFound") + class UserRefreshTokenTest(TestCase): def setUp(self): - self.user = User.objects.create_user(name='ユーザー名', email='example@email.com', password='p4s$W0rd') + self.user = User.objects.create_user( + name="ユーザー名", email="example@email.com", password="p4s$W0rd" + ) self.refresh_token_payload = { - 'uuid': str(self.user.uuid), - 'exp': datetime.utcnow() + settings.JWT_AUTH['JWT_REFRESH_EXPIRATION_DELTA'], - 'iat': datetime.utcnow() + "uuid": str(self.user.uuid), + "exp": datetime.utcnow() + + settings.JWT_AUTH["JWT_REFRESH_EXPIRATION_DELTA"], + "iat": datetime.utcnow(), } - self.refresh_token = jwt.encode(self.refresh_token_payload, settings.JWT_AUTH['JWT_PRIVATE_KEY'], algorithm=settings.JWT_AUTH['JWT_ALGORITHM']) + self.refresh_token = jwt.encode( + self.refresh_token_payload, + settings.JWT_AUTH["JWT_PRIVATE_KEY"], + algorithm=settings.JWT_AUTH["JWT_ALGORITHM"], + ) def test_refresh_token_normal(self): - self.client.cookies['refresh_token'] = self.refresh_token - response = self.client.post(reverse('pong:refresh'), content_type='application/json') + self.client.cookies["refresh_token"] = self.refresh_token + response = self.client.post( + reverse("pong:refresh"), content_type="application/json" + ) self.assertEqual(response.status_code, 200) - self.assertEqual(response.json()['uuid'], str(self.user.uuid)) + self.assertEqual(response.json()["uuid"], str(self.user.uuid)) - token = response.client.cookies['token'].value + token = response.client.cookies["token"].value try: - decoded_token = jwt.decode(token, settings.JWT_AUTH['JWT_PUBLIC_KEY'], algorithms=['RS256']) + decoded_token = jwt.decode( + token, settings.JWT_AUTH["JWT_PUBLIC_KEY"], algorithms=["RS256"] + ) except jwt.ExpiredSignatureError: - self.fail('Token is expired') + self.fail("Token is expired") except jwt.InvalidTokenError: - self.fail('Token is invalid') - self.assertIn('uuid', decoded_token) - self.assertIn('exp', decoded_token) - self.assertIn('iat', decoded_token) - self.assertEqual(decoded_token['uuid'], str(self.user.uuid)) + self.fail("Token is invalid") + self.assertIn("uuid", decoded_token) + self.assertIn("exp", decoded_token) + self.assertIn("iat", decoded_token) + self.assertEqual(decoded_token["uuid"], str(self.user.uuid)) - new_refresh_token = response.client.cookies['refresh_token'].value + new_refresh_token = response.client.cookies["refresh_token"].value try: - decoded_refresh_token = jwt.decode(new_refresh_token, settings.JWT_AUTH['JWT_PUBLIC_KEY'], algorithms=['RS256']) + decoded_refresh_token = jwt.decode( + new_refresh_token, + settings.JWT_AUTH["JWT_PUBLIC_KEY"], + algorithms=["RS256"], + ) except jwt.ExpiredSignatureError: - self.fail('Refresh token is expired') + self.fail("Refresh token is expired") except jwt.InvalidTokenError: - self.fail('Refresh token is invalid') - self.assertIn('uuid', decoded_refresh_token) - self.assertIn('exp', decoded_refresh_token) - self.assertIn('iat', decoded_refresh_token) - self.assertEqual(decoded_refresh_token['uuid'], str(self.user.uuid)) + self.fail("Refresh token is invalid") + self.assertIn("uuid", decoded_refresh_token) + self.assertIn("exp", decoded_refresh_token) + self.assertIn("iat", decoded_refresh_token) + self.assertEqual(decoded_refresh_token["uuid"], str(self.user.uuid)) def test_refresh_token_expired(self): expired_refresh_token_payload = { - 'uuid': str(self.user.uuid), - 'exp': datetime.utcnow() - timedelta(seconds=1), - 'iat': datetime.utcnow() - timedelta(days=30) + "uuid": str(self.user.uuid), + "exp": datetime.utcnow() - timedelta(seconds=1), + "iat": datetime.utcnow() - timedelta(days=30), } - expired_refresh_token = jwt.encode(expired_refresh_token_payload, settings.JWT_AUTH['JWT_PRIVATE_KEY'], algorithm=settings.JWT_AUTH['JWT_ALGORITHM']) - self.client.cookies['refresh_token'] = expired_refresh_token - response = self.client.post(reverse('pong:refresh'), content_type='application/json') + expired_refresh_token = jwt.encode( + expired_refresh_token_payload, + settings.JWT_AUTH["JWT_PRIVATE_KEY"], + algorithm=settings.JWT_AUTH["JWT_ALGORITHM"], + ) + self.client.cookies["refresh_token"] = expired_refresh_token + response = self.client.post( + reverse("pong:refresh"), content_type="application/json" + ) self.assertEqual(response.status_code, 400) - self.assertEqual(response.json()['message'], 'Refresh token has expired') - self.assertEqual(response.json()['status'], 'invalidParams') + self.assertEqual(response.json()["message"], "Refresh token has expired") + self.assertEqual(response.json()["status"], "invalidParams") def test_refresh_token_invalid(self): - self.client.cookies['refresh_token'] = 'invalid_token' - response = self.client.post(reverse('pong:refresh'), content_type='application/json') + self.client.cookies["refresh_token"] = "invalid_token" + response = self.client.post( + reverse("pong:refresh"), content_type="application/json" + ) self.assertEqual(response.status_code, 400) - self.assertEqual(response.json()['message'], 'Invalid refresh token') - self.assertEqual(response.json()['status'], 'invalidParams') + self.assertEqual(response.json()["message"], "Invalid refresh token") + self.assertEqual(response.json()["status"], "invalidParams") + class UserVerifyTokenTest(TestCase): def setUp(self): - self.user = User.objects.create_user(name='ユーザー名', email='example@email.com', password='p4s$W0rd') + self.user = User.objects.create_user( + name="ユーザー名", email="example@email.com", password="p4s$W0rd" + ) self.token_payload = { - 'uuid': str(self.user.uuid), - 'exp': datetime.utcnow() + settings.JWT_AUTH['JWT_EXPIRATION_DELTA'], - 'iat': datetime.utcnow() + "uuid": str(self.user.uuid), + "exp": datetime.utcnow() + settings.JWT_AUTH["JWT_EXPIRATION_DELTA"], + "iat": datetime.utcnow(), } - self.token = jwt.encode(self.token_payload, settings.JWT_AUTH['JWT_PRIVATE_KEY'], algorithm=settings.JWT_AUTH['JWT_ALGORITHM']) + self.token = jwt.encode( + self.token_payload, + settings.JWT_AUTH["JWT_PRIVATE_KEY"], + algorithm=settings.JWT_AUTH["JWT_ALGORITHM"], + ) self.refresh_token_payload = { - 'uuid': str(self.user.uuid), - 'exp': datetime.utcnow() + settings.JWT_AUTH['JWT_REFRESH_EXPIRATION_DELTA'], - 'iat': datetime.utcnow() + "uuid": str(self.user.uuid), + "exp": datetime.utcnow() + + settings.JWT_AUTH["JWT_REFRESH_EXPIRATION_DELTA"], + "iat": datetime.utcnow(), } - self.refresh_token = jwt.encode(self.refresh_token_payload, settings.JWT_AUTH['JWT_PRIVATE_KEY'], algorithm=settings.JWT_AUTH['JWT_ALGORITHM']) - self.client.cookies['token'] = self.token - self.client.cookies['refresh_token'] = self.refresh_token + self.refresh_token = jwt.encode( + self.refresh_token_payload, + settings.JWT_AUTH["JWT_PRIVATE_KEY"], + algorithm=settings.JWT_AUTH["JWT_ALGORITHM"], + ) + self.client.cookies["token"] = self.token + self.client.cookies["refresh_token"] = self.refresh_token def test_verify_token_normal(self): - response = self.client.post(reverse('pong:verify'), content_type='application/json') + response = self.client.post( + reverse("pong:verify"), content_type="application/json" + ) self.assertEqual(response.status_code, 200) - self.assertEqual(response.json()['uuid'], str(self.user.uuid)) + self.assertEqual(response.json()["uuid"], str(self.user.uuid)) def test_token_expired(self): expired_token_payload = { - 'uuid': str(self.user.uuid), - 'exp': datetime.utcnow() - timedelta(seconds=1), - 'iat': datetime.utcnow() - timedelta(days=30) + "uuid": str(self.user.uuid), + "exp": datetime.utcnow() - timedelta(seconds=1), + "iat": datetime.utcnow() - timedelta(days=30), } - expired_token = jwt.encode(expired_token_payload, settings.JWT_AUTH['JWT_PRIVATE_KEY'], algorithm=settings.JWT_AUTH['JWT_ALGORITHM']) - self.client.cookies['token'] = expired_token - response = self.client.post(reverse('pong:verify'), content_type='application/json') + expired_token = jwt.encode( + expired_token_payload, + settings.JWT_AUTH["JWT_PRIVATE_KEY"], + algorithm=settings.JWT_AUTH["JWT_ALGORITHM"], + ) + self.client.cookies["token"] = expired_token + response = self.client.post( + reverse("pong:verify"), content_type="application/json" + ) self.assertEqual(response.status_code, 401) - self.assertEqual(response.json()['message'], 'unauthorized') - self.assertEqual(response.json()['status'], 'unauthorized') + self.assertEqual(response.json()["message"], "unauthorized") + self.assertEqual(response.json()["status"], "unauthorized") diff --git a/pong/pong/views/test_users.py b/pong/pong/views/test_users.py index 09400c89..b5d00cd8 100644 --- a/pong/pong/views/test_users.py +++ b/pong/pong/views/test_users.py @@ -3,41 +3,57 @@ from pong.models import User import jwt from django.conf import settings -from http.cookies import SimpleCookie -from datetime import datetime, timedelta -import uuid - -class GetUserTest(TestCase): - def setUp(self): - self.user = User.objects.create_user(name='ユーザー名', email='example@email.com', password='p4s$W0rd') - self.token_payload = { - 'uuid': str(self.user.uuid), - 'exp': datetime.utcnow() + settings.JWT_AUTH['JWT_EXPIRATION_DELTA'], - 'iat': datetime.utcnow() - } - self.token = jwt.encode(self.token_payload, settings.JWT_AUTH['JWT_PRIVATE_KEY'], algorithm=settings.JWT_AUTH['JWT_ALGORITHM']) - self.refresh_token_payload = { - 'uuid': str(self.user.uuid), - 'exp': datetime.utcnow() + settings.JWT_AUTH['JWT_REFRESH_EXPIRATION_DELTA'], - 'iat': datetime.utcnow() - } - self.refresh_token = jwt.encode(self.refresh_token_payload, settings.JWT_AUTH['JWT_PRIVATE_KEY'], algorithm=settings.JWT_AUTH['JWT_ALGORITHM']) - self.client.cookies['token'] = self.token - self.client.cookies['refresh_token'] = self.refresh_token - - def test_get_user_normal(self): - response = self.client.get(reverse('pong:get_user', kwargs={'uuid': self.user.uuid}), content_type='application/json') - self.assertEqual(response.status_code, 200) - - def test_get_user_not_allowed_method(self): - response = self.client.post(reverse('pong:get_user', kwargs={'uuid': self.user.uuid}), content_type='application/json') - self.assertEqual(response.status_code, 400) - - - def test_user_not_found(self): - response = self.client.get(reverse('pong:get_user', kwargs={'uuid': 'b4cf1ef4-1cab-490b-a32c-f6528f95c796'}), content_type='application/json') - self.assertEqual(response.status_code, 404) - - +from datetime import datetime +class GetUserTest(TestCase): + def setUp(self): + self.user = User.objects.create_user( + name="ユーザー名", email="example@email.com", password="p4s$W0rd" + ) + self.token_payload = { + "uuid": str(self.user.uuid), + "exp": datetime.utcnow() + settings.JWT_AUTH["JWT_EXPIRATION_DELTA"], + "iat": datetime.utcnow(), + } + self.token = jwt.encode( + self.token_payload, + settings.JWT_AUTH["JWT_PRIVATE_KEY"], + algorithm=settings.JWT_AUTH["JWT_ALGORITHM"], + ) + self.refresh_token_payload = { + "uuid": str(self.user.uuid), + "exp": datetime.utcnow() + + settings.JWT_AUTH["JWT_REFRESH_EXPIRATION_DELTA"], + "iat": datetime.utcnow(), + } + self.refresh_token = jwt.encode( + self.refresh_token_payload, + settings.JWT_AUTH["JWT_PRIVATE_KEY"], + algorithm=settings.JWT_AUTH["JWT_ALGORITHM"], + ) + self.client.cookies["token"] = self.token + self.client.cookies["refresh_token"] = self.refresh_token + + def test_get_user_normal(self): + response = self.client.get( + reverse("pong:get_user", kwargs={"uuid": self.user.uuid}), + content_type="application/json", + ) + self.assertEqual(response.status_code, 200) + + def test_get_user_not_allowed_method(self): + response = self.client.post( + reverse("pong:get_user", kwargs={"uuid": self.user.uuid}), + content_type="application/json", + ) + self.assertEqual(response.status_code, 400) + + def test_user_not_found(self): + response = self.client.get( + reverse( + "pong:get_user", kwargs={"uuid": "b4cf1ef4-1cab-490b-a32c-f6528f95c796"} + ), + content_type="application/json", + ) + self.assertEqual(response.status_code, 404) diff --git a/pong/pong/views/users.py b/pong/pong/views/users.py index 5fd12306..03bd4229 100644 --- a/pong/pong/views/users.py +++ b/pong/pong/views/users.py @@ -1,33 +1,28 @@ -import json from django.http.response import JsonResponse -from pong.models import UserManager from pong.models import User from django.views.decorators.csrf import csrf_exempt -from datetime import datetime, timedelta -import jwt -from django.conf import settings -from django.http.response import HttpResponse -from pong.middleware.auth import jwt_exempt, getUserByJwt + @csrf_exempt def get_user(request, uuid): - if request.method != 'GET': - return JsonResponse({ - 'message': 'Method is not allowed', - 'status': 'invalidParams' - }, status=400) + if request.method != "GET": + return JsonResponse( + {"message": "Method is not allowed", "status": "invalidParams"}, status=400 + ) - user = User.objects.filter(uuid=uuid).first() + user = User.objects.filter(uuid=uuid).first() - if not user: - return JsonResponse({ - 'message': 'User not found', - 'status': 'userNotFound' - }, status=404) + if not user: + return JsonResponse( + {"message": "User not found", "status": "userNotFound"}, status=404 + ) - return JsonResponse({ - 'uuid': user.uuid, - 'name': user.name, - 'email': user.email, - 'icon': user.icon.url, - }, status=200) + return JsonResponse( + { + "uuid": user.uuid, + "name": user.name, + "email": user.email, + "icon": user.icon.url, + }, + status=200, + ) diff --git a/pong/ruff.toml b/pong/ruff.toml new file mode 100644 index 00000000..710fad0f --- /dev/null +++ b/pong/ruff.toml @@ -0,0 +1,2 @@ +[lint] +ignore = ["E402"] \ No newline at end of file From 2e14ea06f9689e1b72a1cd1c004b501fde999561 Mon Sep 17 00:00:00 2001 From: Sota Okuno Date: Sat, 20 Jul 2024 17:25:25 +0900 Subject: [PATCH 5/8] =?UTF-8?q?refactor:=20=E3=83=95=E3=82=A1=E3=82=A4?= =?UTF-8?q?=E3=83=AB=E3=81=AE=E6=9C=AB=E5=B0=BE=E3=81=AB=E6=94=B9=E8=A1=8C?= =?UTF-8?q?=E3=81=8C=E3=81=AA=E3=81=84=E3=81=A8warning=E3=82=92=E5=87=BA?= =?UTF-8?q?=E3=81=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pong/ruff.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pong/ruff.toml b/pong/ruff.toml index 710fad0f..d53325f7 100644 --- a/pong/ruff.toml +++ b/pong/ruff.toml @@ -1,2 +1,3 @@ [lint] -ignore = ["E402"] \ No newline at end of file +ignore = ["E402"] +select = ["W292"] \ No newline at end of file From 93241ea8c2cece2d5e9b8ba915440bd015b023a4 Mon Sep 17 00:00:00 2001 From: Sota Okuno Date: Sat, 20 Jul 2024 17:29:24 +0900 Subject: [PATCH 6/8] =?UTF-8?q?refactor:=20jobs=E3=81=AE=E5=90=8D=E5=89=8D?= =?UTF-8?q?=E3=82=92syntax-check-python=E3=81=AB=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ruff.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ruff.yml b/.github/workflows/ruff.yml index d9768941..e8d39b4c 100644 --- a/.github/workflows/ruff.yml +++ b/.github/workflows/ruff.yml @@ -2,6 +2,7 @@ name: ruff on: push jobs: ruff: + name: syntax-check-python runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 From 5a25d8dd7beebaf242623bbd1accecc7b50fd0cc Mon Sep 17 00:00:00 2001 From: Sota Okuno Date: Sat, 20 Jul 2024 17:29:51 +0900 Subject: [PATCH 7/8] =?UTF-8?q?refactor:=20=E3=83=95=E3=82=A1=E3=82=A4?= =?UTF-8?q?=E3=83=AB=E6=9C=AB=E5=B0=BE=E3=81=AB=E6=94=B9=E8=A1=8C=E3=82=92?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ruff.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ruff.yml b/.github/workflows/ruff.yml index e8d39b4c..a90308a0 100644 --- a/.github/workflows/ruff.yml +++ b/.github/workflows/ruff.yml @@ -8,4 +8,4 @@ jobs: - uses: actions/checkout@v4 - uses: chartboost/ruff-action@v1 with: - args: 'format --check' \ No newline at end of file + args: 'format --check' From 6771e6747320e629b1edf980628d80a2aab93678 Mon Sep 17 00:00:00 2001 From: Sota Okuno Date: Sat, 20 Jul 2024 17:40:33 +0900 Subject: [PATCH 8/8] =?UTF-8?q?refactor:=20=E3=83=95=E3=82=A1=E3=82=A4?= =?UTF-8?q?=E3=83=AB=E6=9C=AB=E5=B0=BE=E3=81=AB=E7=A9=BA=E8=A1=8C=E3=82=92?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pong/ruff.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pong/ruff.toml b/pong/ruff.toml index d53325f7..96dc024e 100644 --- a/pong/ruff.toml +++ b/pong/ruff.toml @@ -1,3 +1,3 @@ [lint] ignore = ["E402"] -select = ["W292"] \ No newline at end of file +select = ["W292"]