Skip to content

Commit

Permalink
Merge branch 'production' of github.com:zakarm/ft_transcendence into …
Browse files Browse the repository at this point in the history
…production
  • Loading branch information
Mushigarou committed Apr 26, 2024
2 parents 5320d96 + 8e3e5f4 commit 3eb6238
Show file tree
Hide file tree
Showing 74 changed files with 1,389 additions and 634 deletions.
25 changes: 25 additions & 0 deletions .github/workflows/pylint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: Pylint

on: [push]

jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.9", "3.10", "3.12"]
steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v3
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
cp app/back-end/requirements.txt .
pip install -r requirements.txt
pip install pylint pylint-django
- name: Analysing the code with pylint
run: |
cp app/back-end/.pylintrc .
pylint $(git ls-files '*.py') --rcfile=.pylintrc --django-settings=ft_transcendence.settings
100 changes: 70 additions & 30 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,43 +1,83 @@
dc-up = docker-compose up
dc-upb = docker-compose up --build
dc-down = docker-compose down
dp = docker ps
drm = docker system prune -af
dc-downp = docker-compose down --rmi all --volumes --remove-orphans
# Makefile
# Set default shell
SHELL := /bin/bash

all: mkdir upb
# Set variables
DOCKER_COMPOSE := docker-compose
DOCKER_COMPOSE_FILE := docker-compose.yml
DOCKER_COMPOSE_FLAGS := --env-file .env
MAJOR ?= 3
MINOR ?= 8
PATCH ?= 0
VERSION := $(MAJOR).$(MINOR).$(PATCH)
DATA_DIR := /Users/${USER}/Desktop/data

mkdir:
mkdir -p /Users/$(USER)/Desktop/data
# Define colors
GREEN := $(shell tput -Txterm setaf 2)
YELLOW := $(shell tput -Txterm setaf 3)
RESET := $(shell tput -Txterm sgr0)

rmdir: down
rm -rf /Users/$(USER)/Desktop/data
# Define targets
.PHONY: help build up down restart logs

up:
$(dc-up)
help: ## Show this help message
@echo "Usage: make [target]"
@echo ""
@echo "Targets:"
@egrep '^(.+)\:\ ##\ (.+)' $(MAKEFILE_LIST) | column -t -c 2 -s ':#'

upb:
$(dc-upb)
build: ## Build Docker images
@echo "$(GREEN)Building Docker images...$(RESET)"
$(DOCKER_COMPOSE) -f $(DOCKER_COMPOSE_FILE) $(DOCKER_COMPOSE_FLAGS) build --no-cache

down:
$(dc-down)
up: ## Start Docker containers
@echo "$(GREEN)Starting Docker containers...$(RESET)"
$(DOCKER_COMPOSE) -f $(DOCKER_COMPOSE_FILE) $(DOCKER_COMPOSE_FLAGS) up -d

ps:
$(dp)
down: ## Stop and remove Docker containers
@echo "$(YELLOW)Stopping and removing Docker containers...$(RESET)"
$(DOCKER_COMPOSE) -f $(DOCKER_COMPOSE_FILE) $(DOCKER_COMPOSE_FLAGS) down

prune:
$(drm)
restart: down up ## Restart Docker containers

front-end:
docker-compose up -d front-end
logs: ## View logs from Docker containers
@echo "$(GREEN)Viewing logs from Docker containers...$(RESET)"
$(DOCKER_COMPOSE) -f $(DOCKER_COMPOSE_FILE) $(DOCKER_COMPOSE_FLAGS) logs -f

back-end:
docker-compose up -d back-end
version: ## Show the current version
@echo "$(GREEN)Current version: $(VERSION)$(RESET)"

data-base:
docker-compose up -d data-base
remove-data-dir: ## Remove the data directory
@echo "$(YELLOW)Removing data directory $(DATA_DIR)...$(RESET)"
rm -rf $(DATA_DIR)

clean:
$(dc-downp)
create-data-dir: ## Create the data directory
@echo "$(GREEN)Creating data directory $(DATA_DIR)...$(RESET)"
mkdir -p $(DATA_DIR)

fclean: rmdir prune
remove-volumes: ## Remove Docker volumes
@echo "$(YELLOW)Removing Docker volumes...$(RESET)"
$(DOCKER_COMPOSE) -f $(DOCKER_COMPOSE_FILE) $(DOCKER_COMPOSE_FLAGS) down -v

.PHONY: clean-all
clean-all: down remove-volumes remove-data-dir ## Clean up Docker containers, volumes, and data directory
@echo "$(GREEN)Clean up completed.$(RESET)"

clean: clean-all ## Remove build artifacts and temporary files
@echo "$(YELLOW)Cleaning up build artifacts and temporary files...$(RESET)"
rm -rf ./app/front-end/node_modules
rm -rf ./app/front-end/.next
rm -rf ./app/back-end/__pycache__
find . -type f -name '*.pyc' -delete
find . -type d -name '__pycache__' -delete

.PHONY: update-version
update-version: ## Update the version number
@read -p "Enter new version (MAJOR.MINOR.PATCH): " NEW_VERSION; \
MAJOR=$$(echo $$NEW_VERSION | cut -d'.' -f1); \
MINOR=$$(echo $$NEW_VERSION | cut -d'.' -f2); \
PATCH=$$(echo $$NEW_VERSION | cut -d'.' -f3); \
sed -i '' "s/MAJOR ?= [0-9]*/MAJOR ?= $$MAJOR/" $(MAKEFILE_LIST); \
sed -i '' "s/MINOR ?= [0-9]*/MINOR ?= $$MINOR/" $(MAKEFILE_LIST); \
sed -i '' "s/PATCH ?= [0-9]*/PATCH ?= $$PATCH/" $(MAKEFILE_LIST); \
echo "$(GREEN)Version updated to $$NEW_VERSION$(RESET)"
33 changes: 33 additions & 0 deletions app/back-end/.pylintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
[MASTER]
load-plugins=pylint_django
django-settings-module=ft_transcendence.settings

[MESSAGES CONTROL]
disable=missing-docstring,
import-error,
invalid-name,
too-few-public-methods,
abstract-method,
arguments-renamed,
too-many-locals,
too-many-branches,
too-many-statements,
import-outside-toplevel,
arguments-differ,
no-else-return

[FORMAT]
max-line-length=120

[DESIGN]
max-parents=13

[TYPECHECK]
ignored-modules=django.contrib.admin

[REPORTS]
output-format=colorized
reports=no

[BASIC]
good-names=i,j,k,ex,pk,Run,_
3 changes: 0 additions & 3 deletions app/back-end/authentication/admin.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
"""Module providing a User class."""
from django.contrib import admin
from .models import User

class UserAdmin(admin.ModelAdmin):
"Class for user display in admin page"
list_display = ('id', 'email', 'username','first_name', 'last_name', 'is_staff', 'is_active',
'date_joined', 'is_superuser', 'last_login', 'image_url',
'cover_url', 'location')
search_fields = ('id', 'email', 'username','first_name', 'last_name', 'is_staff', 'is_active',
'date_joined', 'is_superuser', 'last_login',
'image_url', 'cover_url', 'location')
readonly_fields = ('id', 'date_joined', 'last_login')

filter_horizontal = ()
list_filter = ()
fieldsets = ()
Expand Down
8 changes: 0 additions & 8 deletions app/back-end/authentication/models.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
"""Module providing the data from the database mounted to models"""
from django.db import models
from django.contrib.auth.models import PermissionsMixin, AbstractBaseUser, BaseUserManager

class UserManager(BaseUserManager):
"""Class representing a User Manager"""

def create_user(self, email, password=None, **extra_fields):
"""Function create a User"""
if not email:
Expand All @@ -16,18 +13,15 @@ def create_user(self, email, password=None, **extra_fields):
return user

def create_superuser(self, email, password=None, **extra_fields):
"""Function create a Superuser"""
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(email, password, **extra_fields)


class User(AbstractBaseUser, PermissionsMixin):
"""Class representing a User"""
username = models.CharField(max_length=150)
email = models.EmailField(max_length=254, unique=True)
password = models.CharField(max_length=128)
Expand All @@ -54,6 +48,4 @@ class User(AbstractBaseUser, PermissionsMixin):
objects = UserManager()
USERNAME_FIELD = 'email'
class Meta:
"""Class to change the behavior of your model fields"""
db_table = 'authentication_users'

26 changes: 17 additions & 9 deletions app/back-end/authentication/serializer.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
"""Module providing rest serailizers"""
import sys
import requests
import pyotp
Expand Down Expand Up @@ -31,7 +30,8 @@ def validate(self, data):
if user.is_2fa_enabled:
user.two_fa_secret_key = pyotp.random_base32()
user.save()
url_code = pyotp.totp.TOTP(user.two_fa_secret_key).provisioning_uri(name = user.email, issuer_name = "ft_transcendence")
url_code = pyotp.totp.TOTP(user.two_fa_secret_key).provisioning_uri(
name = user.email, issuer_name = "ft_transcendence")
data['user'] = user
data['url_code'] = url_code
return data
Expand All @@ -55,15 +55,20 @@ def validate(self, data):

class SocialAuthSerializer(serializers.Serializer):
def validate(self, data):
"""
Validate method for SocialAuth serializer
"""
token = self.context.get('access_token')
platform = self.context.get('platform')
headers = {'Authorization': f'Bearer {token}'}
if platform == 'github':
try:
response = requests.get('https://api.github.com/user', headers=headers)
response = requests.get('https://api.github.com/user',
headers=headers, timeout=10000)
response.raise_for_status()
user_info = response.json()
email_response = requests.get('https://api.github.com/user/emails', headers=headers)
email_response = requests.get('https://api.github.com/user/emails',
headers=headers, timeout=10000)
email_response.raise_for_status()
email_info = email_response.json()
email = next((email['email'] for email in email_info if email['primary']), None)
Expand All @@ -89,8 +94,8 @@ def validate(self, data):
raise serializers.ValidationError("Email already exists") from e
elif platform == 'google':
try :
response = requests.get('https://www.googleapis.com/oauth2/v1/userinfo?alt=json', headers=headers,
timeout=1000)
response = requests.get('https://www.googleapis.com/oauth2/v1/userinfo?alt=json',
headers=headers,timeout=10000)
response.raise_for_status()
user_info = response.json()
email = user_info['email']
Expand All @@ -106,13 +111,14 @@ def validate(self, data):
data['email'] = email
return data
except requests.exceptions.RequestException as e:
raise serializers.ValidationError("Failed to fetch user data from Google")
raise serializers.ValidationError("Failed to fetch user data from Google")
except IntegrityError as e:
raise serializers.ValidationError("Email already exists") from e
elif platform == "42":
try:
print(headers, file=sys.stderr)
response = requests.get('https://api.intra.42.fr/v2/me', headers=headers, timeout=1000)
response = requests.get('https://api.intra.42.fr/v2/me', headers=headers,
timeout=1000)
response.raise_for_status()
user_info = response.json()
email = user_info['email']
Expand All @@ -127,6 +133,8 @@ def validate(self, data):
data['email'] = email
return data
except requests.exceptions.RequestException as e:
raise serializers.ValidationError("Failed to fetch user data from 42")
raise serializers.ValidationError("Failed to fetch user data from 42")
except IntegrityError as e:
raise serializers.ValidationError("Email already exists") from e
else :
return None
39 changes: 17 additions & 22 deletions app/back-end/authentication/tests.py
Original file line number Diff line number Diff line change
@@ -1,36 +1,31 @@
from django.test import TestCase
from rest_framework.test import APIClient, force_authenticate
from rest_framework.test import APIClient
from rest_framework import status
from .models import User
import requests_mock
from django.test import TestCase, RequestFactory
from .views import GithubLogin
import jwt
from django.test import Client

class SignInTest(TestCase):
def setUp(self):
self.client = APIClient()
self.user = User.objects.create_user( email='zakariaemrabet48@gmail.com', password='admin')

def test_login(self):
response = self.client.post('/api/sign-in', {'email': 'zakariaemrabet48@gmail.com', 'password': 'admin'}, format='json')
response = self.client.post('/api/sign-in',
{'email': 'zakariaemrabet48@gmail.com',
'password': 'admin'}, format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertIn('tokens', response.data)
self.assertIn('access', response.data['tokens'])
self.assertIn('refresh', response.data['tokens'])
self.assertIn('access', response.data)
self.assertIn('refresh', response.data)
self.assertIn('email', response.data)
self.assertIn('is_2fa_enabled', response.data)


class GithubLoginTest(TestCase):
class SignUpTest(TestCase):
def setUp(self):
self.client = Client()
self.view = GithubLogin.as_view()
self.client = APIClient()

@requests_mock.Mocker()
def test_github_login(self, m):
m.get('https://api.github.com/user', json={'login': 'zakarm', 'id': 1})
m.get('https://api.github.com/user/emails', json=[{'email': 'zakariaemrabet1@gmail.com', 'primary': True, 'verified': True}])
response = self.client.post('/api/github', {'token': 'testtoken'}, format='json')
self.assertEqual(response.status_code, 200)
decoded_jwt = jwt.decode(response.data['access'], options={"verify_signature": False})
self.assertEqual(decoded_jwt['user_id'], 1)
def test_signup(self):
response = self.client.post('/api/sign-up',
{'email': 'zakariaemrabet48@gmail.com',
'username': 'testuser',
'password': 'admin'}, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertIn('email', response.data)
19 changes: 12 additions & 7 deletions app/back-end/authentication/urls.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
from django.urls import path, include, re_path
from .views import *
"""
Module providing urls utils
"""
from django.urls import path, re_path
from .views import (SignIn2Fa, SignInView, SocialAuthExchangeView,
SocialAuthRedirectView, SignUpView, SignOutView)

urlpatterns = [
path("sign-up", SignUpView.as_view(), name="sign-up"),
path("sign-in", SignInView.as_view(), name="sign-in"),
path("sign-out", SignUpView.as_view(), name="sign-out"),
path("sign-out", SignOutView.as_view(), name="sign-out"),

# re_path(r'^social/(?P<platform>(github|42|google))/auth$', SocialAuthView.as_view(), name='social-auth'),
path('two-fa', SignIn2Fa.as_view(), name="two-fa"),
re_path(r'^social/(?P<platform>(github|42|google))/redirect$', SocialAuthRedirectView.as_view(), name='social-redirect'),
re_path(r'^social/(?P<platform>(github|42|google))/callback$', SocialAuthExchangeView.as_view(), name='social-callback'),
]
re_path(r'^social/(?P<platform>(github|42|google))/redirect$',
SocialAuthRedirectView.as_view(), name='social-redirect'),
re_path(r'^social/(?P<platform>(github|42|google))/callback$',
SocialAuthExchangeView.as_view(), name='social-callback'),
]
Loading

0 comments on commit 3eb6238

Please sign in to comment.