Skip to content

Commit

Permalink
Platform Migration (#22)
Browse files Browse the repository at this point in the history
  • Loading branch information
ashwin1111 authored Dec 21, 2021
1 parent 135331b commit d87fa2e
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 86 deletions.
44 changes: 17 additions & 27 deletions fyle_rest_auth/authentication.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
import json
from typing import Dict

import requests

from django.contrib.auth import get_user_model
from django.conf import settings
from django.core.cache import cache

from rest_framework.authentication import BaseAuthentication
from rest_framework import exceptions
from rest_framework.exceptions import AuthenticationFailed, ValidationError

from .helpers import get_fyle_admin
from .models import AuthToken
from .utils import AuthUtils

Expand All @@ -36,9 +33,9 @@ def authenticate(self, request):
user = User.objects.get(email=user['email'], user_id=user['user_id'])
AuthToken.objects.get(user=user)
except User.DoesNotExist:
raise exceptions.AuthenticationFailed('User not found for this token')
raise ValidationError('User not found for this token')
except AuthToken.DoesNotExist:
raise exceptions.AuthenticationFailed('Login details not found for the user')
raise ValidationError('Login details not found for the user')

return user, None

Expand All @@ -61,11 +58,11 @@ def validate_token(access_token_string: str, origin_address: str) -> Dict:
:return:
"""
if not access_token_string:
raise exceptions.AuthenticationFailed('Access token missing')
raise ValidationError('Access token missing')

access_token_tokenizer = access_token_string.split(' ')
if not access_token_tokenizer or len(access_token_tokenizer) != 2 or access_token_tokenizer[0] != 'Bearer':
raise exceptions.AuthenticationFailed('Invalid access token structure')
raise ValidationError('Invalid access token structure')

unique_key_generator = access_token_tokenizer[1].split('.')
email_unique_key = 'email_{0}'.format(unique_key_generator[2])
Expand All @@ -77,30 +74,23 @@ def validate_token(access_token_string: str, origin_address: str) -> Dict:
if not (email and user):
cache.delete_many([email_unique_key, user_unique_key])

fyle_base_url = settings.FYLE_BASE_URL
my_profile_uri = '{0}/api/tpa/v1/employees/my_profile'.format(fyle_base_url)
api_headers = {
'Authorization': '{0}'.format(access_token_string),
'X-Forwarded-For': origin_address
}

response = requests.get(my_profile_uri, headers=api_headers)
try:
employee_info = get_fyle_admin(access_token_string.split(' ')[1], origin_address)
except Exception:
raise AuthenticationFailed('Invalid access token')

if response.status_code == 200:
result = json.loads(response.text)['data']
cache.set(email_unique_key, employee_info['data']['user']['email'])
cache.set(user_unique_key, employee_info['data']['user']['id'])

cache.set(email_unique_key, result['employee_email'])
cache.set(user_unique_key, result['user_id'])

return {
'email': result['employee_email'],
'user_id': result['user_id']
}
return {
'email': employee_info['data']['user']['email'],
'user_id': employee_info['data']['user']['id']
}

elif email and user:
return {
'email': email,
'user_id': user
}

raise exceptions.AuthenticationFailed('Invalid access token')
raise AuthenticationFailed('Invalid access token')
58 changes: 46 additions & 12 deletions fyle_rest_auth/helpers.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
from rest_framework.exceptions import AuthenticationFailed
from typing import Dict

from rest_framework.exceptions import ValidationError

from django.contrib.auth import get_user_model
from django.conf import settings
from django.utils.module_loading import import_string

from .utils import AuthUtils
from .utils import AuthUtils, post_request, get_request
from .models import AuthToken

auth = AuthUtils()
Expand All @@ -13,16 +16,16 @@ def validate_code_and_login(request):
authorization_code = request.data.get('code')
try:
if not authorization_code:
raise AuthenticationFailed('authorization code not found')
raise ValidationError('authorization code not found')

tokens = auth.generate_fyle_refresh_token(authorization_code=authorization_code)

employee_info = auth.get_fyle_user(tokens['refresh_token'], auth.get_origin_address(request))
employee_info = get_fyle_admin(tokens['access_token'], auth.get_origin_address(request))
users = get_user_model()

user, _ = users.objects.get_or_create(
user_id=employee_info['user_id'],
email=employee_info['employee_email']
user_id=employee_info['data']['user']['id'],
email=employee_info['data']['user']['email']
)

AuthToken.objects.update_or_create(
Expand All @@ -38,23 +41,26 @@ def validate_code_and_login(request):
return tokens

except Exception as error:
raise AuthenticationFailed(error)
raise ValidationError(error)


def validate_and_refresh_token(request):
refresh_token = request.data.get('refresh_token')
try:
if not refresh_token:
raise AuthenticationFailed('refresh token not found')
raise ValidationError('refresh token not found')

tokens = auth.refresh_access_token(refresh_token)

employee_info = auth.get_fyle_user(refresh_token, auth.get_origin_address(request))
employee_info = get_fyle_admin(tokens['access_token'], auth.get_origin_address(request))
users = get_user_model()

user = users.objects.filter(email=employee_info['employee_email'], user_id=employee_info['user_id']).first()
user = users.objects.filter(
email=employee_info['data']['user']['email'], user_id=employee_info['data']['user']['id']
).first()

if not user:
raise AuthenticationFailed('User record not found, please login')
raise ValidationError('User record not found, please login')

auth_token = AuthToken.objects.get(user=user)
auth_token.refresh_token = refresh_token
Expand All @@ -67,4 +73,32 @@ def validate_and_refresh_token(request):
return tokens

except Exception as error:
raise AuthenticationFailed(error)
raise ValidationError(error)


def get_cluster_domain(access_token: str, origin_address: str = None) -> str:
"""
Get cluster domain name from fyle
:param access_token: (str)
:return: cluster_domain (str)
"""
cluster_api_url = '{0}/oauth/cluster/'.format(settings.FYLE_BASE_URL)

return post_request(cluster_api_url, {}, access_token, origin_address)['cluster_domain']


def get_fyle_admin(access_token: str, origin_address: str = None) -> Dict:
"""
Get user profile from fyle
:param access_token: (str)
:return: user_profile (dict)
"""
cluster_domain = get_cluster_domain(access_token, origin_address)

profile_api_url = '{}/platform/v1beta/spender/my_profile'.format(cluster_domain)
employee_detail = get_request(profile_api_url, access_token, origin_address)

if 'ADMIN' in employee_detail['data']['roles']:
return employee_detail
else:
raise Exception('User is not an admin')
89 changes: 46 additions & 43 deletions fyle_rest_auth/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,53 @@
import json
from typing import Dict

import requests
from django.conf import settings

from fylesdk import FyleSDK, UnauthorizedClientError, NotFoundClientError, InternalServerError, WrongParamsError
import requests


def post_request(url, body, access_token: str = None, origin_address: str = None) -> Dict:
"""
Create a HTTP post request.
"""
api_headers = {
'content-type': 'application/json',
'X-Forwarded-For': origin_address
}

if access_token:
api_headers['Authorization'] = 'Bearer {0}'.format(access_token)

response = requests.post(
url,
headers=api_headers,
data=body
)

if response.status_code == 200:
return json.loads(response.text)
else:
raise Exception(response.text)


def get_request(url, access_token, origin_address: str = None):
"""
Create a HTTP get request.
"""
api_headers = {
'Authorization': 'Bearer {0}'.format(access_token),
'X-Forwarded-For': origin_address
}

response = requests.get(
url,
headers=api_headers
)

if response.status_code == 200:
return json.loads(response.text)
else:
raise Exception(response.text)


class AuthUtils:
Expand Down Expand Up @@ -44,48 +87,8 @@ def refresh_access_token(self, refresh_token: str) -> Dict:
'refresh_token': refresh_token
}

return self.post(url=self.token_url, body=api_data)
return post_request(self.token_url, api_data)

def get_fyle_user(self, refresh_token: str, origin_address: str = None) -> Dict:
"""
Get Fyle user detail
"""
connection = FyleSDK(
base_url=self.base_url,
client_id=self.client_id,
client_secret=self.client_secret,
refresh_token=refresh_token,
origin_address=origin_address
)

employee_detail = connection.Employees.get_my_profile()['data']

return employee_detail

@staticmethod
def post(url, body):
"""
Send Post request
"""
response = requests.post(url, data=body)

if response.status_code == 200:
return json.loads(response.text)

elif response.status_code == 401:
raise UnauthorizedClientError('Wrong client secret or/and refresh token', response.text)

elif response.status_code == 404:
raise NotFoundClientError('Client ID doesn\'t exist', response.text)

elif response.status_code == 400:
raise WrongParamsError('Some of the parameters were wrong', response.text)

elif response.status_code == 500:
raise InternalServerError('Internal server error', response.text)

else:
raise InternalServerError('Internal server error', response.text)

@staticmethod
def get_origin_address(request):
Expand Down
1 change: 0 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ charset-normalizer==2.0.8
Django==3.1.14
django-rest-framework==0.1.0
djangorestframework==3.11.2
fylesdk==2.3.0
idna==2.8
isort==4.3.21
lazy-object-proxy==1.4.3
Expand Down
5 changes: 2 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

setuptools.setup(
name='fyle-rest-auth',
version='0.9.0',
version='1.0.0',
author='Shwetabh Kumar',
author_email='[email protected]',
description='Django application to implement OAuth 2.0 using Fyle in Django rest framework',
Expand All @@ -19,8 +19,7 @@
url='https://github.com/fylein/fyle-rest-auth',
packages=setuptools.find_packages(),
install_requires=['requests>=2.25.0', 'django>=3.0.2',
'django-rest-framework==0.1.0',
'fylesdk>=2.2.0'],
'django-rest-framework==0.1.0'],
include_package_data=True,
classifiers=[
'Framework :: Django',
Expand Down

0 comments on commit d87fa2e

Please sign in to comment.