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

Firebase Cloud Messaging #52

Merged
merged 14 commits into from
Oct 14, 2020
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ pyaescrypt = "*"
python-decouple = "*"
drf-yasg = "*"
pylint-django = "*"
fcm-django = "*"

[requires]
python_version = "3.8"
548 changes: 329 additions & 219 deletions Pipfile.lock

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions authentication/models.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
from django.db import models
from django.contrib.auth.models import User
from django.contrib.auth import get_user_model


class UserProfile(models.Model):
uid = models.CharField(max_length=64)
user = models.OneToOneField(User, on_delete=models.CASCADE)
user = models.OneToOneField(get_user_model(), on_delete=models.CASCADE)
name = models.CharField(max_length=255)
email = models.EmailField(max_length=255)
phone_number = models.CharField(max_length=15, null=True, blank=True)
Expand Down
28 changes: 18 additions & 10 deletions authentication/serializers.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from django.core.validators import RegexValidator
from django.contrib.auth import get_user_model
from rest_framework import serializers
from drf_yasg.utils import swagger_serializer_method
from workshop.serializers import ClubSerializer
from .utils import Student, FirebaseAPI
from .models import UserProfile, User
from .models import UserProfile

phone_regex = RegexValidator(
regex=r'^\+\d{9,15}$',
Expand All @@ -24,8 +25,9 @@ def access_token_validate(self, access_token):
"""
try:
return FirebaseAPI.verify_id_token(access_token)
except:
raise serializers.ValidationError("Invalid Firebase token!")
except serializers.ValidationError as e:
raise serializers.ValidationError(
"Invalid Firebase token!") from e

def validate(self, attrs):
id_token = attrs.get('id_token', None)
Expand All @@ -43,7 +45,7 @@ def validate(self, attrs):
raise serializers.ValidationError(
"Please login using @itbhu.ac.in student email id only")
name = jwt['name']
user = User()
user = get_user_model()
user.username = jwt['uid']
user.email = email
user.save()
Expand All @@ -60,7 +62,8 @@ def validate(self, attrs):


class ProfileSerializer(serializers.ModelSerializer):
phone_number = serializers.CharField(max_length=15, validators=[phone_regex,], allow_blank=True)
phone_number = serializers.CharField(max_length=15, validators=[
phone_regex, ], allow_blank=True)
subscriptions = serializers.SerializerMethodField()
club_privileges = serializers.SerializerMethodField()

Expand Down Expand Up @@ -88,7 +91,8 @@ def update(self, instance, validated_data):
# pylint: disable=no-member
instance.name = name
instance.phone_number = phone_number
instance.photo_url = FirebaseAPI.get_photo_url(instance.uid) # update photo_url of user
instance.photo_url = FirebaseAPI.get_photo_url(
instance.uid) # update photo_url of user
instance.save()
return instance

Expand All @@ -103,15 +107,17 @@ class Meta:


class ProfileSearchSerializer(serializers.Serializer):
search_by = serializers.ChoiceField(choices=['name', 'email'], default='email')
search_by = serializers.ChoiceField(
choices=['name', 'email'], default='email')
search_string = serializers.CharField(max_length=255)

def validate_search_string(self, search_string):
"""
Validate the search_string field, length must be greater than 3.
"""
if len(search_string) < 3:
raise serializers.ValidationError("The length of search field must be atleast 3")
raise serializers.ValidationError(
"The length of search field must be atleast 3")
return search_string

def save(self, **kwargs):
Expand All @@ -120,7 +126,9 @@ def save(self, **kwargs):
search_string = data['search_string']
# pylint: disable=no-member
if search_by == 'name':
profile = UserProfile.objects.filter(name__icontains=search_string)[:10]
profile = UserProfile.objects.filter(
name__icontains=search_string)[:10]
elif search_by == 'email':
profile = UserProfile.objects.filter(email__icontains=search_string)[:10]
profile = UserProfile.objects.filter(
email__icontains=search_string)[:10]
return profile
8 changes: 8 additions & 0 deletions authentication/urls.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
from django.urls import path
from django.conf.urls import url, include
from rest_framework.routers import DefaultRouter
from fcm_django.api.rest_framework import FCMDeviceAuthorizedViewSet
from .views import LoginView, ProfileView, ProfileSearchView


router = DefaultRouter()
router.register(r'devices', FCMDeviceAuthorizedViewSet)

urlpatterns = [
path('login/', LoginView.as_view(), name='login'),
path('profile/', ProfileView.as_view(), name='profile'),
path('profile/search/', ProfileSearchView.as_view(), name='profile-search'),
url(r'^', include(router.urls)),
]
7 changes: 4 additions & 3 deletions authentication/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def verify_email(cls, email):
"""
username = email.split('@')[0]
domain = email.split('@')[1]
if domain not in ['itbhu.ac.in',]:
if domain not in ['itbhu.ac.in', ]:
return False
if '.' not in username:
return False
Expand All @@ -78,8 +78,9 @@ def verify_id_token(cls, id_token):
try:
decoded_token = auth.verify_id_token(id_token)
return decoded_token
except ValueError:
raise ValidationError('Invalid Firebase ID Token.', HTTP_422_UNPROCESSABLE_ENTITY)
except ValueError as e:
raise ValidationError(
'Invalid Firebase ID Token.', HTTP_422_UNPROCESSABLE_ENTITY) from e

@classmethod
def delete_user_by_uid(cls, uid):
Expand Down
9 changes: 9 additions & 0 deletions authentication/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from rest_framework import status
from rest_framework.response import Response
from rest_framework.authtoken.models import Token
from fcm_django.models import FCMDevice
from workshop.permissions import AllowAnyClubHead
from .models import UserProfile
from .serializers import (
Expand All @@ -17,6 +18,14 @@ def create_auth_token(user):
return token


def send_notification(message, title):
"""
Send notification to all users using Firebase Cloud Messaging
"""
devices = FCMDevice.objects.all()
devices.send_message(title=title, body=message)
return True

class LoginView(generics.GenericAPIView):
authentication_classes = []
permission_classes = (permissions.AllowAny, )
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ gunicorn==19.9.0
pyAesCrypt==0.4.3
pylint-django==2.0.13
python-decouple==3.1
fcm-django==0.3.4
13 changes: 13 additions & 0 deletions workshops/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,14 @@
else:
SECRET_KEY = config('SECRET_KEY')

# SECURITY WARNING: keep the secret key used in production secret!
if DEBUG:
FCM_SERVER_KEY = ''
else:
FCM_SERVER_KEY = config('FCM_SERVER_KEY')



ALLOWED_HOSTS = ['*']


Expand All @@ -51,6 +59,7 @@
'corsheaders',
'authentication',
'workshop',
'fcm_django',
'team'
]

Expand Down Expand Up @@ -95,6 +104,10 @@
)
}

FCM_DJANGO_SETTINGS = {
"FCM_SERVER_KEY": FCM_SERVER_KEY
}

SWAGGER_SETTINGS = {
'SECURITY_DEFINITIONS': {
'Token': {
Expand Down