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

#44 Login implementado #84

Merged
merged 16 commits into from
Sep 30, 2019
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,6 @@ __pycache__/
local_settings.py
db.sqlite3
media
.vscode/
env/
migrations/
25 changes: 8 additions & 17 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
version: '3.7'

volumes:
pgdata:

services:

db:
image: postgres:11.5
container_name: acacia_db
env_file:
- .env
ports:
- "5432:5432"
networks:
- acacia_network
image: postgres

acacia-back:
container_name: acacia_backend
Expand All @@ -20,18 +17,12 @@ services:
cd src/ &&
python manage.py makemigrations &&
python manage.py migrate &&
chmod +x scripts/create_superuser.sh &&
./scripts/create_superuser.sh &&
python manage.py runserver 0.0.0.0:8080"
ports:
- "8080:8080"
volumes:
- .:/code
env_file:
- .env
networks:
- acacia_network
depends_on:
- db

networks:
acacia_network:
name: acacia_network
- db
10 changes: 7 additions & 3 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
Django==2.2.4
djangorestframework==3.10.0
Django==2.2
djangorestframework==3.10
django-phonenumber-field==3.0
phonenumbers==8.10
djangorestframework_simplejwt
django-cors-headers
psycopg2==2.8.3
coverage
coverage
48 changes: 40 additions & 8 deletions src/acacia/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
"""

import os
from scripts.wait_for_db import start_services
from django.utils.translation import ugettext_lazy as _

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
Expand All @@ -25,18 +27,26 @@
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = []
ALLOWED_HOSTS = ['0.0.0.0']

AUTH_USER_MODEL = 'users.User'

# Application definition

INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',

# libs
'rest_framework',
'rest_framework.authtoken',
'corsheaders',

# my apps
'users',
]

MIDDLEWARE = [
Expand All @@ -47,6 +57,8 @@
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
]

ROOT_URLCONF = 'acacia.urls'
Expand All @@ -67,19 +79,27 @@
},
]

WSGI_APPLICATION = 'acacia.wsgi.application'


# Database
# https://docs.djangoproject.com/en/2.2/ref/settings/#databases
WSGI_APPLICATION = 'acacia.wsgi.application'

DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'postgres',
'USER': 'postgres',
'HOST': 'db',
'PORT': '5432',
}
}

start_services()

LANGUAGES = (
('en-us', _('English')),
('pt-br', _('Brazilian Portuguese')),
('fr-CA', _('French Canadian')),
)


# Password validation
# https://docs.djangoproject.com/en/2.2/ref/settings/#auth-password-validators
Expand Down Expand Up @@ -118,3 +138,15 @@
# https://docs.djangoproject.com/en/2.2/howto/static-files/

STATIC_URL = '/static/'


REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework_simplejwt.authentication.JWTAuthentication',
]
}

CORS_ORIGIN_WHITELIST = [
"http://localhost:8080",
"http://localhost:8000",
]
5 changes: 3 additions & 2 deletions src/acacia/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path
from django.urls import path, include

urlpatterns = [
path('admin/', admin.site.urls),
]
path('users/', include('users.urls')),
]
9 changes: 9 additions & 0 deletions src/scripts/create_superuser.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# default superuser
# email: 'admin@admin.com'
# password: 'password'
echo "
from django.contrib.auth import get_user_model;
User = get_user_model();

if not len(User.objects.all()):
User.objects.create_superuser(username='admin', email='admin@admin.com', password='password')" | python manage.py shell
60 changes: 60 additions & 0 deletions src/scripts/wait_for_db.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import importlib
import os
import time

import logging

SERVICES_STARTED = False

log = logging.getLogger('ej')


def start_services():
global SERVICES_STARTED

if SERVICES_STARTED:
return

start_postgres()

SERVICES_STARTED = True



log = logging.getLogger('ej')


def start_postgres():
settings_path = os.environ['DJANGO_SETTINGS_MODULE']
settings = importlib.import_module(settings_path)

db = settings.DATABASES['default']
dbname = db['NAME']
user = db['USER']
host = db['HOST']

for _ in range(100):
if can_connect(dbname, user, host):
log.info("Postgres is available. Continuing...")
return
log.warning('Postgres is unavailable. Retrying in 0.5 seconds')
time.sleep(0.5)

log.critical('Maximum number of attempts connecting to postgres database')
raise RuntimeError('could not connect to database')


def can_connect(dbname, user, host):
import psycopg2

try:
psycopg2.connect(
dbname=dbname,
user=user,
host=host
)

except psycopg2.OperationalError:
return False

return True
Empty file added src/users/__init__.py
Empty file.
15 changes: 15 additions & 0 deletions src/users/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from django.contrib import admin
from .models import User

class UserAdmin(admin.ModelAdmin):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Usando ModelAdmin ele coloca a hash na senha? (Como faz o UserAdmin do django.contrib.auth.admin?

Copy link
Collaborator

@durvalcarvalho durvalcarvalho Sep 27, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Essa classe User que criamos está usando os mecanismos de senha do user padrão do Django, esses mecanismos vem da herança no AbstractUser, lá no arquivo models.py

class User(AbstractUser):

Essa classe UserAdmin foi criada para podermos acessar o painel do admin do django ( /admin/ ). Já que não estamos usando o User nativo do django é preciso explicitar os campos que irão aparecer no painel.

image

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

só pra esclarecer, o useradmin é um tipo de modeladmin mais adequado, mas ok

list_display = (
'email',
'phone_number',
'birth',
'email',
'username',
)

search_fields = ('email',)

admin.site.register(User, UserAdmin)
5 changes: 5 additions & 0 deletions src/users/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from django.apps import AppConfig


class UsersConfig(AppConfig):
name = 'users'
50 changes: 50 additions & 0 deletions src/users/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Generated by Django 2.2.4 on 2019-09-24 19:30
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vitorcx @shayanealcantara talvez devessemos criar uma issue futura pra gerenciarmos migrações (até então não vai dar problema, mas pode dar no futuro, quando estivermos trabalhando em várias funcinoalidades e branchs, pois elas são geradas sequenciamente e commitá-las pode dar algum problema

Copy link
Collaborator

@durvalcarvalho durvalcarvalho Sep 27, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Eu pensei em não criar commits em nenhuma migração. Eu acho que enquanto o nosso banco de dados não estiver em produção, não tem problema ficarmos zerando o banco e criando todas as migrações do zero.


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


class Migration(migrations.Migration):

initial = True

dependencies = [
('auth', '0011_update_proxy_permissions'),
]

operations = [
migrations.CreateModel(
name='User',
fields=[
('id', models.AutoField(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=30, verbose_name='first name')),
('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
('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')),
('email', models.EmailField(max_length=254, unique=True, verbose_name='email address')),
('phone_number', phonenumber_field.modelfields.PhoneNumberField(blank=True, max_length=128, region=None)),
('bio', models.TextField(blank=True)),
('birth', models.DateField(blank=True, null=True)),
('speaks_french', models.BooleanField(default=False, help_text='Designates If the user speaks French.', verbose_name='speaks french')),
('speaks_english', models.BooleanField(default=False, help_text='Designates If the user speaks English.', verbose_name='speaks english')),
('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()),
],
),
]
Empty file.
44 changes: 44 additions & 0 deletions src/users/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
from django.db import models
from django.contrib.auth.models import AbstractUser
from django.utils.translation import gettext_lazy as _
from phonenumber_field.modelfields import PhoneNumberField


class User(AbstractUser):

email = models.EmailField(
_('email address'),
unique=True,
blank=False,
error_messages={
'unique': 'A user with that email already exists.',
}
)

phone_number = PhoneNumberField(blank=True, null=True)
bio = models.TextField(blank=True, null=True)
birth = models.DateField(blank=True, null=True)

is_verified = models.BooleanField(
'verified',
default=True,
help_text=(
'Set to true when the user have verified its email address.'
)
)

speaks_french = models.BooleanField(
_('speaks french'),
default=False,
help_text=_('Designates If the user speaks French.'),
)

speaks_english = models.BooleanField(
_('speaks english'),
default=False,
help_text=_('Designates If the user speaks English.'),
)

EMAIL_FIELD = 'email'
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username']
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Username como campo obrigatório, existe algum motivo?

Copy link
Collaborator

@durvalcarvalho durvalcarvalho Sep 27, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sim! O Django.

Eu tirei ele durante alguns teste, mas quando tento entrar no painel do admin do django a aplicação quebra. Aparentemente os templates do django admin usam a variável {{user.username}}, e se ela não existir quebra tudo.

Uma solução seria sobrescrever esse template, mas eu não achei que seria uma boa ideia...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

teria uma solução mais interessante, mas fica pra outra sprint, criar com lambda, a partir do email do usuário um username

Loading