Skip to content

Commit

Permalink
Firebase Cloud Messaging (#52)
Browse files Browse the repository at this point in the history
* fcm

* FCM

* FCM

* FCM Authorized

* removed lint errors

* removed lint errors 2

* removed lint errors 2

* Update Pipfile

* Update Pipfile

* updated pipenv

* updated pipenv

* Removed lint errors

* Removed lint errors

* Removed lint errors
  • Loading branch information
davidgarg20 authored Oct 14, 2020
1 parent 98a2115 commit 4739219
Show file tree
Hide file tree
Showing 9 changed files with 385 additions and 234 deletions.
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

0 comments on commit 4739219

Please sign in to comment.