version 0.0.5.1: added integration with facebook, refactored a lot
This commit is contained in:
parent
2101a5ae5e
commit
22f2de94f2
|
|
@ -19,6 +19,11 @@ class UserAdmin(BaseUserAdmin):
|
||||||
(None, {'fields': ('email', 'password',)}),
|
(None, {'fields': ('email', 'password',)}),
|
||||||
(_('Personal info'), {
|
(_('Personal info'), {
|
||||||
'fields': ('username', 'first_name', 'last_name', )}),
|
'fields': ('username', 'first_name', 'last_name', )}),
|
||||||
|
(_('Subscription'), {
|
||||||
|
'fields': (
|
||||||
|
'newsletter',
|
||||||
|
)
|
||||||
|
}),
|
||||||
(_('Important dates'), {'fields': ('last_login', 'date_joined')}),
|
(_('Important dates'), {'fields': ('last_login', 'date_joined')}),
|
||||||
(_('Permissions'), {
|
(_('Permissions'), {
|
||||||
'fields': (
|
'fields': (
|
||||||
|
|
|
||||||
18
apps/account/migrations/0002_user_newsletter.py
Normal file
18
apps/account/migrations/0002_user_newsletter.py
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 2.2.4 on 2019-08-08 08:37
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('account', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='user',
|
||||||
|
name='newsletter',
|
||||||
|
field=models.NullBooleanField(default=True),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -10,6 +10,18 @@ class UserManager(BaseUserManager):
|
||||||
|
|
||||||
use_in_migrations = False
|
use_in_migrations = False
|
||||||
|
|
||||||
|
def make(self, username: str, email: str,
|
||||||
|
password: str, newsletter: bool) -> object:
|
||||||
|
"""Register new user"""
|
||||||
|
obj = self.model(
|
||||||
|
email=email,
|
||||||
|
username=username,
|
||||||
|
password=password,
|
||||||
|
newsletter=newsletter
|
||||||
|
)
|
||||||
|
obj.save()
|
||||||
|
return obj
|
||||||
|
|
||||||
|
|
||||||
class UserQuerySet(models.QuerySet):
|
class UserQuerySet(models.QuerySet):
|
||||||
"""Extended queryset for User model."""
|
"""Extended queryset for User model."""
|
||||||
|
|
@ -23,6 +35,7 @@ class User(ImageMixin, AbstractUser):
|
||||||
"""Base user model."""
|
"""Base user model."""
|
||||||
email = models.EmailField(_('email address'), blank=True,
|
email = models.EmailField(_('email address'), blank=True,
|
||||||
null=True, default=None)
|
null=True, default=None)
|
||||||
|
newsletter = models.NullBooleanField(default=True)
|
||||||
|
|
||||||
EMAIL_FIELD = 'email'
|
EMAIL_FIELD = 'email'
|
||||||
USERNAME_FIELD = 'username'
|
USERNAME_FIELD = 'username'
|
||||||
|
|
|
||||||
0
apps/account/serializers/__init__.py
Normal file
0
apps/account/serializers/__init__.py
Normal file
|
|
@ -1,7 +0,0 @@
|
||||||
from modeltranslation.translator import TranslationOptions, register
|
|
||||||
from .models import User
|
|
||||||
|
|
||||||
|
|
||||||
@register(User)
|
|
||||||
class UserModelTranslation(TranslationOptions):
|
|
||||||
"""Translation for mode User"""
|
|
||||||
0
apps/account/urls/__init__.py
Normal file
0
apps/account/urls/__init__.py
Normal file
|
|
@ -1,11 +1,12 @@
|
||||||
"""Account app urlconf."""
|
"""Account app urlconf."""
|
||||||
from django.urls import path
|
from django.urls import path
|
||||||
|
|
||||||
from account import views
|
from account.views import web as views
|
||||||
|
|
||||||
app_name = 'account'
|
app_name = 'account'
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('user/', views.UserView.as_view(), name='user_get_update'),
|
path('user/', views.UserView.as_view(), name='user_get_update'),
|
||||||
path('device/', views.FCMDeviceViewSet.as_view(), name='fcm_device_create'),
|
path('device/', views.FCMDeviceViewSet.as_view(), name='fcm_device_create'),
|
||||||
|
# path('reset-password/', views.ResetPasswordView.as_view(), name='reset-password'),
|
||||||
]
|
]
|
||||||
0
apps/account/views/__init__.py
Normal file
0
apps/account/views/__init__.py
Normal file
|
|
@ -4,7 +4,7 @@ from rest_framework import permissions
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
|
|
||||||
from account import models
|
from account import models
|
||||||
from account import serializers
|
from account.serializers import web as serializers
|
||||||
|
|
||||||
|
|
||||||
class UserView(generics.RetrieveUpdateAPIView):
|
class UserView(generics.RetrieveUpdateAPIView):
|
||||||
1
apps/authorization/__init__.py
Normal file
1
apps/authorization/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
default_app_config = 'authorization.apps.AuthorizationConfig'
|
||||||
3
apps/authorization/admin.py
Normal file
3
apps/authorization/admin.py
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
# Register your models here.
|
||||||
8
apps/authorization/apps.py
Normal file
8
apps/authorization/apps.py
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
"""Authorization app config."""
|
||||||
|
from django.apps import AppConfig
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
|
class AuthorizationConfig(AppConfig):
|
||||||
|
name = 'authorization'
|
||||||
|
verbose_name = _('Authorization')
|
||||||
39
apps/authorization/migrations/0001_initial.py
Normal file
39
apps/authorization/migrations/0001_initial.py
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
# Generated by Django 2.2.4 on 2019-08-09 05:47
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
import oauth2_provider.generators
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Application',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(primary_key=True, serialize=False)),
|
||||||
|
('client_id', models.CharField(db_index=True, default=oauth2_provider.generators.generate_client_id, max_length=100, unique=True)),
|
||||||
|
('redirect_uris', models.TextField(blank=True, help_text='Allowed URIs list, space separated')),
|
||||||
|
('client_type', models.CharField(choices=[('confidential', 'Confidential'), ('public', 'Public')], max_length=32)),
|
||||||
|
('authorization_grant_type', models.CharField(choices=[('authorization-code', 'Authorization code'), ('implicit', 'Implicit'), ('password', 'Resource owner password-based'), ('client-credentials', 'Client credentials')], max_length=32)),
|
||||||
|
('client_secret', models.CharField(blank=True, db_index=True, default=oauth2_provider.generators.generate_client_secret, max_length=255)),
|
||||||
|
('name', models.CharField(blank=True, max_length=255)),
|
||||||
|
('skip_authorization', models.BooleanField(default=False)),
|
||||||
|
('created', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('updated', models.DateTimeField(auto_now=True)),
|
||||||
|
('source', models.PositiveSmallIntegerField(choices=[(0, 'Mobile'), (1, 'Web')], default=0, verbose_name='Source')),
|
||||||
|
('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='authorization_application', to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
'swappable': 'OAUTH2_PROVIDER_APPLICATION_MODEL',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
0
apps/authorization/migrations/__init__.py
Normal file
0
apps/authorization/migrations/__init__.py
Normal file
39
apps/authorization/models.py
Normal file
39
apps/authorization/models.py
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
from django.db import models
|
||||||
|
from oauth2_provider.models import AbstractApplication
|
||||||
|
from oauth2_provider import models as oauth2_models
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
|
# Create your models here.
|
||||||
|
class ApplicationQuerySet(models.QuerySet):
|
||||||
|
"""Application queryset"""
|
||||||
|
def get_by_natural_key(self, client_id):
|
||||||
|
return self.get(client_id=client_id)
|
||||||
|
|
||||||
|
def by_source(self, source: int):
|
||||||
|
"""Filter by source parameter"""
|
||||||
|
return self.filter(source=source)
|
||||||
|
|
||||||
|
|
||||||
|
class ApplicationManager(oauth2_models.ApplicationManager):
|
||||||
|
"""Application manager"""
|
||||||
|
|
||||||
|
|
||||||
|
class Application(AbstractApplication):
|
||||||
|
"""Custom oauth2 application model"""
|
||||||
|
MOBILE = 0
|
||||||
|
WEB = 1
|
||||||
|
|
||||||
|
SOURCES = (
|
||||||
|
(MOBILE, _('Mobile')),
|
||||||
|
(WEB, _('Web')),
|
||||||
|
)
|
||||||
|
source = models.PositiveSmallIntegerField(choices=SOURCES, default=MOBILE,
|
||||||
|
verbose_name=_('Source'))
|
||||||
|
objects = ApplicationManager.from_queryset(ApplicationQuerySet)()
|
||||||
|
|
||||||
|
class Meta(AbstractApplication.Meta):
|
||||||
|
swappable = "OAUTH2_PROVIDER_APPLICATION_MODEL"
|
||||||
|
|
||||||
|
def natural_key(self):
|
||||||
|
return (self.client_id,)
|
||||||
0
apps/authorization/serializers/__init__.py
Normal file
0
apps/authorization/serializers/__init__.py
Normal file
10
apps/authorization/serializers/common.py
Normal file
10
apps/authorization/serializers/common.py
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
"""Common serializer for application authorization"""
|
||||||
|
from rest_framework import serializers
|
||||||
|
from authorization.models import Application
|
||||||
|
|
||||||
|
|
||||||
|
class SocialSignUpSerialzier(serializers.Serializer):
|
||||||
|
"""Serializer for signing up"""
|
||||||
|
|
||||||
|
source = serializers.ChoiceField(choices=Application.SOURCES)
|
||||||
|
token = serializers.CharField(max_length=255)
|
||||||
81
apps/authorization/serializers/web.py
Normal file
81
apps/authorization/serializers/web.py
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
"""Serializers for application authorization"""
|
||||||
|
from rest_framework import serializers
|
||||||
|
from rest_framework.authentication import authenticate
|
||||||
|
from rest_framework import validators as rest_validators
|
||||||
|
from account import models as account_models
|
||||||
|
from django.contrib.auth import password_validation as password_validators
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
|
class AuthTokenClassicSerializer(serializers.Serializer):
|
||||||
|
username = serializers.CharField(
|
||||||
|
label=_('Username'),
|
||||||
|
required=False
|
||||||
|
)
|
||||||
|
password = serializers.CharField(
|
||||||
|
label=_("Password"),
|
||||||
|
style={'input_type': 'password'},
|
||||||
|
trim_whitespace=False
|
||||||
|
)
|
||||||
|
email = serializers.EmailField(
|
||||||
|
label=_("Email"),
|
||||||
|
required=False
|
||||||
|
)
|
||||||
|
|
||||||
|
def validate(self, attrs):
|
||||||
|
username = attrs.get('username')
|
||||||
|
password = attrs.get('password')
|
||||||
|
email = attrs.get('email')
|
||||||
|
|
||||||
|
if username and password:
|
||||||
|
user = authenticate(request=self.context.get('request'),
|
||||||
|
username=username, password=password)
|
||||||
|
elif email and password:
|
||||||
|
user = authenticate(request=self.context.get('request'),
|
||||||
|
email=email, password=password)
|
||||||
|
else:
|
||||||
|
msg = _('Must include "phone" and "password".')
|
||||||
|
raise serializers.ValidationError(msg, code='authorization')
|
||||||
|
if user:
|
||||||
|
# From Django 1.10 onwards the `authenticate` call simply
|
||||||
|
# returns `None` for is_active=False users.
|
||||||
|
# (Assuming the default `ModelBackend` authentication backend.)
|
||||||
|
if not user.is_active:
|
||||||
|
msg = _('User account is disabled.')
|
||||||
|
raise serializers.ValidationError(msg, code='authorization')
|
||||||
|
attrs['user'] = user
|
||||||
|
return attrs
|
||||||
|
|
||||||
|
|
||||||
|
class SignUpSerializer(serializers.ModelSerializer):
|
||||||
|
"""Serializer for signing up user"""
|
||||||
|
email = serializers.CharField(
|
||||||
|
validators=(rest_validators.UniqueValidator(queryset=account_models.User.objects.all()), ),
|
||||||
|
write_only=True
|
||||||
|
)
|
||||||
|
username = serializers.CharField(
|
||||||
|
validators=(rest_validators.UniqueValidator(queryset=account_models.User.objects.all()), ),
|
||||||
|
write_only=True
|
||||||
|
)
|
||||||
|
password = serializers.CharField(write_only=True)
|
||||||
|
newsletter = serializers.BooleanField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Meta-class"""
|
||||||
|
model = account_models.User
|
||||||
|
fields = ('email', 'username',
|
||||||
|
'newsletter', 'password')
|
||||||
|
|
||||||
|
def validate_password(self, data):
|
||||||
|
"""Custom password validation"""
|
||||||
|
try:
|
||||||
|
password_validators.validate_password(password=data)
|
||||||
|
except serializers.ValidationError as e:
|
||||||
|
raise serializers.ValidationError(e)
|
||||||
|
else:
|
||||||
|
return data
|
||||||
|
|
||||||
|
def create(self, validated_data):
|
||||||
|
"""Override create method"""
|
||||||
|
obj = account_models.User.objects.make(**validated_data)
|
||||||
|
return obj
|
||||||
3
apps/authorization/tests.py
Normal file
3
apps/authorization/tests.py
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
||||||
0
apps/authorization/tests/__init__.py
Normal file
0
apps/authorization/tests/__init__.py
Normal file
3
apps/authorization/tests/tests.py
Normal file
3
apps/authorization/tests/tests.py
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
||||||
0
apps/authorization/urls/__init__.py
Normal file
0
apps/authorization/urls/__init__.py
Normal file
46
apps/authorization/urls/common.py
Normal file
46
apps/authorization/urls/common.py
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
"""Common url routing for application authorization"""
|
||||||
|
from django.conf import settings
|
||||||
|
from django.conf.urls import url, include
|
||||||
|
from django.urls import path
|
||||||
|
from oauth2_provider.views import AuthorizationView
|
||||||
|
from rest_framework_social_oauth2.views import (ConvertTokenView, TokenView,
|
||||||
|
RevokeTokenView, invalidate_sessions)
|
||||||
|
from social_core.utils import setting_name
|
||||||
|
from social_django import views as social_django_views
|
||||||
|
|
||||||
|
from authorization.views import web as views
|
||||||
|
|
||||||
|
extra = getattr(settings, setting_name('TRAILING_SLASH'), True) and '/' or ''
|
||||||
|
|
||||||
|
|
||||||
|
app_name = 'social'
|
||||||
|
|
||||||
|
urlpatterns_social_django = [
|
||||||
|
# authentication / association
|
||||||
|
url(r'^login/(?P<backend>[^/]+){0}$'.format(extra), social_django_views.auth,
|
||||||
|
name='begin'),
|
||||||
|
url(r'^complete/(?P<backend>[^/]+){0}$'.format(extra), social_django_views.complete,
|
||||||
|
name='complete'),
|
||||||
|
# disconnection
|
||||||
|
url(r'^disconnect/(?P<backend>[^/]+){0}$'.format(extra), social_django_views.disconnect,
|
||||||
|
name='disconnect'),
|
||||||
|
url(r'^disconnect/(?P<backend>[^/]+)/(?P<association_id>\d+){0}$'
|
||||||
|
.format(extra), social_django_views.disconnect, name='disconnect_individual'),
|
||||||
|
]
|
||||||
|
|
||||||
|
urlpatterns_rest_framework_social_oauth2 = [
|
||||||
|
url(r'^authorize/?$', AuthorizationView.as_view(), name="authorize"),
|
||||||
|
url(r'^token/?$', TokenView.as_view(), name="token"),
|
||||||
|
url('', include('social_django.urls', namespace="social")),
|
||||||
|
url(r'^convert-token/?$', ConvertTokenView.as_view(), name="convert_token"),
|
||||||
|
url(r'^revoke-token/?$', RevokeTokenView.as_view(), name="revoke_token"),
|
||||||
|
url(r'^invalidate-sessions/?$', invalidate_sessions, name="invalidate_sessions")
|
||||||
|
]
|
||||||
|
|
||||||
|
urlpatterns_api = [
|
||||||
|
path('social/signup/', views.SocialSignUpView.as_view(), name='signup'),
|
||||||
|
]
|
||||||
|
|
||||||
|
urlpatterns = urlpatterns_api + \
|
||||||
|
urlpatterns_social_django + \
|
||||||
|
urlpatterns_rest_framework_social_oauth2
|
||||||
6
apps/authorization/urls/web.py
Normal file
6
apps/authorization/urls/web.py
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
"""Url routing for application authorization"""
|
||||||
|
|
||||||
|
app_name = 'auth'
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
]
|
||||||
3
apps/authorization/views.py
Normal file
3
apps/authorization/views.py
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
from django.shortcuts import render
|
||||||
|
|
||||||
|
# Create your views here.
|
||||||
0
apps/authorization/views/__init__.py
Normal file
0
apps/authorization/views/__init__.py
Normal file
6
apps/authorization/views/common.py
Normal file
6
apps/authorization/views/common.py
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
"""Common views for application authorization"""
|
||||||
|
from django.shortcuts import render
|
||||||
|
from rest_framework import views as rest_views
|
||||||
|
|
||||||
|
|
||||||
|
# Create your views here.
|
||||||
82
apps/authorization/views/web.py
Normal file
82
apps/authorization/views/web.py
Normal file
|
|
@ -0,0 +1,82 @@
|
||||||
|
"""Views for application Account"""
|
||||||
|
import json
|
||||||
|
|
||||||
|
from braces.views import CsrfExemptMixin
|
||||||
|
from oauth2_provider.settings import oauth2_settings
|
||||||
|
from oauth2_provider.views.mixins import OAuthLibMixin
|
||||||
|
from rest_framework import permissions
|
||||||
|
from authorization.models import Application
|
||||||
|
from django.conf import settings
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
from rest_framework.response import Response
|
||||||
|
from rest_framework.generics import GenericAPIView
|
||||||
|
from rest_framework_social_oauth2.oauth2_backends import KeepRequestCore
|
||||||
|
from rest_framework_social_oauth2.oauth2_endpoints import SocialTokenServer
|
||||||
|
from authorization.serializers import common as serializers
|
||||||
|
from utils import exceptions as utils_exceptions
|
||||||
|
|
||||||
|
|
||||||
|
# Create your views here.
|
||||||
|
class SocialSignUpView(CsrfExemptMixin, OAuthLibMixin, GenericAPIView):
|
||||||
|
"""
|
||||||
|
Implements an endpoint to convert a provider token to an access token
|
||||||
|
|
||||||
|
The endpoint is used in the following flows:
|
||||||
|
|
||||||
|
* Authorization code
|
||||||
|
* Client credentials
|
||||||
|
"""
|
||||||
|
server_class = SocialTokenServer
|
||||||
|
validator_class = oauth2_settings.OAUTH2_VALIDATOR_CLASS
|
||||||
|
oauthlib_backend_class = KeepRequestCore
|
||||||
|
permission_classes = (permissions.AllowAny,)
|
||||||
|
serializer_class = serializers.SocialSignUpSerialzier
|
||||||
|
|
||||||
|
def get_client_id(self, source) -> str:
|
||||||
|
"""Get application client id"""
|
||||||
|
qs = Application.objects.by_source(source=source)
|
||||||
|
if qs.exists():
|
||||||
|
return qs.first().client_id
|
||||||
|
else:
|
||||||
|
raise utils_exceptions.SerivceError(data=_('Application is not found'))
|
||||||
|
|
||||||
|
def get_client_secret(self, source) -> str:
|
||||||
|
"""Get application client id"""
|
||||||
|
if source == Application.MOBILE:
|
||||||
|
qs = Application.objects.by_source(source=source)
|
||||||
|
if qs.exists:
|
||||||
|
return qs.first().client_secret
|
||||||
|
else:
|
||||||
|
raise utils_exceptions.SerivceError(
|
||||||
|
data=_('Not found an application with this source'))
|
||||||
|
|
||||||
|
def post(self, request, *args, **kwargs):
|
||||||
|
"""Override POST method"""
|
||||||
|
# Serialize POST-data
|
||||||
|
serializer = self.get_serializer(data=request.data)
|
||||||
|
serializer.is_valid(raise_exception=True)
|
||||||
|
# Set attributes
|
||||||
|
source = serializer.validated_data.get('source')
|
||||||
|
token = serializer.validated_data.get('token')
|
||||||
|
# Set OAuth2 request parameters
|
||||||
|
request_data = {
|
||||||
|
'grant_type': settings.OAUTH2_SOCIAL_AUTH_GRANT_TYPE,
|
||||||
|
'backend': settings.OAUTH2_SOCIAL_AUTH_BACKEND_NAME,
|
||||||
|
'token': token,
|
||||||
|
'client_id': self.get_client_id(source)
|
||||||
|
}
|
||||||
|
# Fill client secret parameter by platform
|
||||||
|
if source == Application.MOBILE:
|
||||||
|
request_data['client_secret'] = self.get_client_secret(source)
|
||||||
|
|
||||||
|
# Use the rest framework `.data` to fake the post body of the django request.
|
||||||
|
request._request.POST = request._request.POST.copy()
|
||||||
|
for key, value in request_data.items():
|
||||||
|
request._request.POST[key] = value
|
||||||
|
|
||||||
|
url, headers, body, status = self.create_token_response(request._request)
|
||||||
|
response = Response(data=json.loads(body), status=status)
|
||||||
|
|
||||||
|
for k, v in headers.items():
|
||||||
|
response[k] = v
|
||||||
|
return response
|
||||||
15
apps/utils/exceptions.py
Normal file
15
apps/utils/exceptions.py
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
from rest_framework import exceptions, status
|
||||||
|
|
||||||
|
|
||||||
|
class SerivceError(exceptions.APIException):
|
||||||
|
"""Service error."""
|
||||||
|
status_code = status.HTTP_503_SERVICE_UNAVAILABLE
|
||||||
|
default_detail = _('Service is temporarily unavailable')
|
||||||
|
|
||||||
|
def __init__(self, data=None):
|
||||||
|
if data:
|
||||||
|
self.default_detail = {
|
||||||
|
**data
|
||||||
|
}
|
||||||
|
super().__init__()
|
||||||
|
|
@ -24,6 +24,14 @@ class ProjectBaseMixin(models.Model):
|
||||||
abstract = True
|
abstract = True
|
||||||
|
|
||||||
|
|
||||||
|
class OAuthProjectMixin:
|
||||||
|
"""OAuth2 mixin for project GM"""
|
||||||
|
|
||||||
|
def get_source(self):
|
||||||
|
"""Method to get of platform"""
|
||||||
|
return NotImplemented
|
||||||
|
|
||||||
|
|
||||||
basemixin_fields = ['created', 'modified']
|
basemixin_fields = ['created', 'modified']
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
36
apps/utils/oauth2.py
Normal file
36
apps/utils/oauth2.py
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
from rest_framework_social_oauth2.backends import DjangoOAuth2
|
||||||
|
from oauth2_provider.models import AccessToken
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
|
||||||
|
|
||||||
|
class GMOAuth2(DjangoOAuth2):
|
||||||
|
|
||||||
|
def get_user_details(self, response):
|
||||||
|
if response.get(self.ID_KEY, None):
|
||||||
|
user = User.objects.get(pk=response[self.ID_KEY])
|
||||||
|
return {'username': user.username,
|
||||||
|
'email': user.email,
|
||||||
|
'fullname': user.get_full_name(),
|
||||||
|
'first_name': user.first_name,
|
||||||
|
'last_name': user.last_name
|
||||||
|
}
|
||||||
|
return {}
|
||||||
|
|
||||||
|
def user_data(self, access_token, *args, **kwargs):
|
||||||
|
try:
|
||||||
|
user_id = AccessToken.objects.get(token=access_token).user.pk
|
||||||
|
return {self.ID_KEY: user_id}
|
||||||
|
except AccessToken.DoesNotExist:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def do_auth(self, access_token, *args, **kwargs):
|
||||||
|
"""Finish the auth process once the access_token was retrieved"""
|
||||||
|
data = self.user_data(access_token, *args, **kwargs)
|
||||||
|
response = kwargs.get('response') or {}
|
||||||
|
response.update(data or {})
|
||||||
|
kwargs.update({'response': response, 'backend': self})
|
||||||
|
if response.get(self.ID_KEY, None):
|
||||||
|
user = User.objects.get(pk=response[self.ID_KEY])
|
||||||
|
return user
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
@ -52,7 +52,7 @@ CONTRIB_APPS = [
|
||||||
|
|
||||||
PROJECT_APPS = [
|
PROJECT_APPS = [
|
||||||
'account.apps.AccountConfig',
|
'account.apps.AccountConfig',
|
||||||
'location.apps.LocationConfig',
|
'authorization.apps.AuthorizationConfig',
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -225,7 +225,7 @@ AUTHENTICATION_BACKENDS = (
|
||||||
'social_core.backends.facebook.FacebookOAuth2',
|
'social_core.backends.facebook.FacebookOAuth2',
|
||||||
|
|
||||||
# django-rest-framework-social-oauth2
|
# django-rest-framework-social-oauth2
|
||||||
'rest_framework_social_oauth2.backends.DjangoOAuth2',
|
'utils.oauth2.GMOAuth2',
|
||||||
|
|
||||||
# Django
|
# Django
|
||||||
'django.contrib.auth.backends.ModelBackend',
|
'django.contrib.auth.backends.ModelBackend',
|
||||||
|
|
@ -236,6 +236,12 @@ OAUTH2_PROVIDER = {
|
||||||
'SCOPES': {'read': 'Read scope', 'write': 'Write scope', 'groups': 'Access to your groups'}
|
'SCOPES': {'read': 'Read scope', 'write': 'Write scope', 'groups': 'Access to your groups'}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Override default OAuth2 namespace
|
||||||
|
DRFSO2_URL_NAMESPACE = 'oauth2'
|
||||||
|
OAUTH2_SOCIAL_AUTH_BACKEND_NAME = 'facebook'
|
||||||
|
OAUTH2_SOCIAL_AUTH_GRANT_TYPE = 'convert_token'
|
||||||
|
OAUTH2_PROVIDER_APPLICATION_MODEL = 'authorization.Application'
|
||||||
|
|
||||||
# SMS Settings
|
# SMS Settings
|
||||||
SMS_EXPIRATION = 5
|
SMS_EXPIRATION = 5
|
||||||
SMS_SEND_DELAY = 30
|
SMS_SEND_DELAY = 30
|
||||||
|
|
|
||||||
|
|
@ -18,10 +18,13 @@ from django.conf.urls.static import static
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.urls import path, include, re_path
|
from django.urls import path, include, re_path
|
||||||
from drf_yasg import openapi
|
from drf_yasg import openapi
|
||||||
from django.conf.urls import url
|
|
||||||
from drf_yasg.views import get_schema_view
|
from drf_yasg.views import get_schema_view
|
||||||
from rest_framework import permissions
|
from rest_framework import permissions
|
||||||
|
|
||||||
|
# URL platform patterns
|
||||||
|
from project.urls import web as web_urlpatterns
|
||||||
|
|
||||||
|
|
||||||
schema_view = get_schema_view(
|
schema_view = get_schema_view(
|
||||||
openapi.Info(
|
openapi.Info(
|
||||||
title="G&M API",
|
title="G&M API",
|
||||||
|
|
@ -48,26 +51,18 @@ urlpatterns_doc = [
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
||||||
urlpatterns_oauth2 = [
|
urlpatterns_social = [
|
||||||
url(r'^auth/', include('rest_framework_social_oauth2.urls')),
|
path('api/oauth2/', include('authorization.urls.common', namespace='oauth2')),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
urlpatterns_api = [
|
|
||||||
path('account/', include('account.urls'), name='account'),
|
|
||||||
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('admin/', admin.site.urls),
|
path('admin/', admin.site.urls),
|
||||||
path('api-auth/', include('rest_framework.urls', namespace='rest_framework')),
|
path('api/web/', include(web_urlpatterns)),
|
||||||
path('api/', include(urlpatterns_api)),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
urlpatterns = urlpatterns + \
|
urlpatterns = urlpatterns + \
|
||||||
static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) + \
|
urlpatterns_social + \
|
||||||
urlpatterns_oauth2
|
static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||||
|
|
||||||
|
|
||||||
if settings.DEBUG:
|
if settings.DEBUG:
|
||||||
22
project/urls/web.py
Normal file
22
project/urls/web.py
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
"""project URL Configuration
|
||||||
|
|
||||||
|
The `urlpatterns` list routes URLs to views. For more information please see:
|
||||||
|
https://docs.djangoproject.com/en/2.2/topics/http/urls/
|
||||||
|
Examples:
|
||||||
|
Function views
|
||||||
|
1. Add an import: from my_app import views
|
||||||
|
2. Add a URL to urlpatterns: path('', views.home, name='home')
|
||||||
|
Class-based views
|
||||||
|
1. Add an import: from other_app.views import Home
|
||||||
|
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
|
||||||
|
Including another URLconf
|
||||||
|
1. Import the include() function: from django.urls import include, path
|
||||||
|
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
||||||
|
"""
|
||||||
|
from django.urls import path, include
|
||||||
|
|
||||||
|
app_name = 'web'
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path('account/', include('account.urls.web')),
|
||||||
|
]
|
||||||
Loading…
Reference in New Issue
Block a user