-
Notifications
You must be signed in to change notification settings - Fork 0
Authentication #2
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
base: develop
Are you sure you want to change the base?
Changes from 3 commits
ad9d057
463b2c5
e2c2e6d
94f434b
7da244b
57757aa
c2fc113
76fb070
a15e6fa
13a3d8c
4a900b2
062d51b
41a27f4
4133062
9db8696
8514208
9d4ac83
5af1ffd
d9a386c
682868c
37937d5
a7f7bc0
94c1c3a
1f0da73
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1 @@ | ||
| web: gunicorn app.wsgi | ||
| web: gunicorn dscountr.wsgi |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| from django.apps import AppConfig | ||
|
|
||
|
|
||
| class MyappConfig(AppConfig): | ||
| name = 'app' |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| from django.contrib import admin | ||
|
|
||
| # Register models here. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| GENDER_CHOICES = ( | ||
| ('M', 'Male'), | ||
| ('F', 'Female'), | ||
| ('A', 'Alien'), | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| # Generated by Django 2.2.3 on 2019-07-02 20:00 | ||
|
|
||
| import app.authentication.models | ||
| from django.db import migrations, models | ||
| 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')), | ||
| ('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(db_index=True, max_length=255, unique=True)), | ||
| ('email', models.EmailField(db_index=True, max_length=254, unique=True)), | ||
| ('phone_number', phonenumber_field.modelfields.PhoneNumberField(db_index=True, max_length=128, region=None, unique=True)), | ||
| ('date_of_birth', models.DateField()), | ||
| ('gender', models.CharField(choices=[('M', 'Male'), ('F', 'Female')], max_length=1)), | ||
| ('password', models.CharField(max_length=128)), | ||
| ('is_active', models.BooleanField(default=True)), | ||
| ('is_staff', models.BooleanField(default=False)), | ||
| ('created_at', models.DateTimeField(auto_now_add=True)), | ||
| ('updated_at', models.DateTimeField(auto_now=True)), | ||
| ('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_plural': 'All Users', | ||
| }, | ||
| managers=[ | ||
| ('objects', app.authentication.models.UserManager()), | ||
| ], | ||
| ), | ||
| ] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,85 @@ | ||
| from django.conf import settings | ||
| from django.contrib.auth.models import ( | ||
| AbstractBaseUser, BaseUserManager, PermissionsMixin | ||
| ) | ||
| from django.db import models | ||
| from django.core.exceptions import ValidationError | ||
| from decouple import config | ||
| from phonenumber_field.modelfields import PhoneNumberField | ||
| from firebase_admin import auth | ||
| from .choices import GENDER_CHOICES | ||
|
|
||
|
|
||
| class UserManager(BaseUserManager): | ||
| use_in_migrations = True | ||
|
|
||
| def createuser(self, **fields): | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Doesn't seem like we're using the |
||
| email = fields.pop('email') | ||
| password = fields.get('password') | ||
|
|
||
| if not email: | ||
| raise ValueError("Email address is required") | ||
| email = self.normalize_email(email) | ||
| user = self.model(email=email, **fields) | ||
| user.set_password(password) | ||
| user.save(using=self._db) | ||
| return user | ||
|
|
||
| def create_superuser(self, **fields): | ||
| """ | ||
| Create and return a `User` with superuser (admin) permissions. | ||
| """ | ||
|
|
||
| user = self.createuser(**fields) | ||
| user.is_superuser = True | ||
| user.is_staff = True | ||
| user.save() | ||
|
|
||
| return user | ||
|
|
||
|
|
||
| class User(AbstractBaseUser, PermissionsMixin): | ||
|
|
||
| username = models.CharField(db_index=True, max_length=255, unique=True) | ||
| email = models.EmailField(db_index=True, unique=True) | ||
| phone_number = PhoneNumberField(db_index=True, unique=True) | ||
| date_of_birth = models.DateField() | ||
| gender = models.CharField(max_length=1, choices=GENDER_CHOICES) | ||
| password = models.CharField(max_length=128) | ||
| is_active = models.BooleanField(default=True) | ||
| is_staff = models.BooleanField(default=False) | ||
| created_at = models.DateTimeField(auto_now_add=True) | ||
| updated_at = models.DateTimeField(auto_now=True) | ||
|
|
||
| # The `USERNAME_FIELD` property specifies the log in field. | ||
| USERNAME_FIELD = 'phone_number' | ||
| REQUIRED_FIELDS = ['username', 'email', 'date_of_birth', ] | ||
|
|
||
| # the UserManager class should manage objects of this type. | ||
| objects = UserManager() | ||
|
|
||
| class Meta: | ||
| verbose_name_plural = "All Users" | ||
|
|
||
| def __str__(self): | ||
| """ | ||
| Returns a string representation of this `User`. | ||
| used when a `User` is printed in the console. | ||
| """ | ||
| return self.email | ||
|
|
||
| @property | ||
| def token(self): | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is the token we get from firebase used for and how long does it take to expire? Either way, unless you're sure you'll get the same token from firebase each time we call it, we should change how we're storing it otherwise it'll change each time you access this property. |
||
| """ | ||
| method allows us to get a user's token | ||
| """ | ||
| return self._generate_firebase_token() | ||
|
|
||
| def _generate_firebase_token(self): | ||
| """ | ||
| Generates a firebase token | ||
| """ | ||
| uid = config('UID') | ||
| token = auth.create_custom_token(uid) | ||
|
|
||
| return token.decode('utf-8') | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| from rest_framework import serializers | ||
| from django.contrib.auth import authenticate | ||
| from rest_framework.validators import UniqueValidator | ||
| from .models import User | ||
| from firebase_admin import auth | ||
| from phonenumber_field.serializerfields import PhoneNumberField | ||
|
|
||
|
|
||
| class RegistrationSerializer(serializers.ModelSerializer): | ||
| password = serializers.CharField( | ||
| max_length=128, | ||
| min_length=8, | ||
| write_only=True | ||
| ) | ||
|
|
||
| token = serializers.CharField(max_length=255, read_only=True) | ||
| phone_number = PhoneNumberField( | ||
| validators=[ | ||
| UniqueValidator(queryset=User.objects.all(), | ||
| message='User with this Phone Number already exists.', | ||
| )],) | ||
|
|
||
| class Meta: | ||
| model = User | ||
| fields = ['id', 'email', 'username', 'password', 'token', | ||
| 'phone_number', 'date_of_birth', 'gender'] | ||
|
|
||
| def create(self, validated_data): | ||
| return User.objects.createuser(**validated_data) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| from django.test import TestCase | ||
|
|
||
| # Write tests here. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| from django.urls import path | ||
| from rest_framework.routers import DefaultRouter, SimpleRouter | ||
| from .views import RegistrationViewSet | ||
|
|
||
| app_name = 'authentication' | ||
|
|
||
|
|
||
| class OptionalTrailingSlashRouter(SimpleRouter): | ||
| def __init_(self, trailing_slash='/?'): | ||
| self.trailing_slash = trailing_slash | ||
| super().__init__() | ||
|
|
||
|
|
||
| router = OptionalTrailingSlashRouter() | ||
| router.register('users', RegistrationViewSet, 'user') | ||
|
|
||
| urlpatterns = router.urls |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| from rest_framework.permissions import AllowAny, IsAdminUser, IsAuthenticated | ||
| from rest_framework.viewsets import ModelViewSet | ||
| from .serializers import (RegistrationSerializer,) | ||
| from . import models | ||
|
|
||
|
|
||
| class RegistrationViewSet(ModelViewSet): | ||
| permission_classes = (AllowAny,) | ||
| serializer_class = RegistrationSerializer | ||
| queryset = models.User.objects.all() | ||
| http_method_names = ['post'] | ||
|
|
||
|
|
||
| class UserViewSet(ModelViewSet): | ||
| permission_classes = (IsAdminUser, IsAuthenticated) | ||
| serializer_class = RegistrationSerializer | ||
| queryset = models.User.objects.all() | ||
| http_method_names = ['post', 'get', 'patch'] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Prefer absolute imports over relative imports
https://www.python.org/dev/peps/pep-0008/#imports