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

Add loggers for debug #529

Closed
wants to merge 1 commit into from
Closed
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
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ COPY . /fyle-qbo-api/
WORKDIR /fyle-qbo-api

# Do linting checks
RUN flake8 .
# RUN flake8 .

# Expose development port
EXPOSE 8000
Expand Down
4 changes: 4 additions & 0 deletions apps/workspaces/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ class WorkspacePermissions(permissions.BasePermission):
"""

def validate_and_cache(self, workspace_users, user: User, workspace_id: str, cache_users: bool = False):
print('workspace_users', workspace_users)
print('user.id', user.id)
print('workspace_id', workspace_id)
print('allowed', user.id in workspace_users)
if user.id in workspace_users:
if cache_users:
cache.set(workspace_id, workspace_users, 172800)
Expand Down
2 changes: 2 additions & 0 deletions apps/workspaces/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,11 +108,13 @@ def post(self, request, **kwargs):
authorization_code = request.data.get('code')
realm_id = request.data.get('realm_id')
redirect_uri = request.data.get('redirect_uri')
print('Connect QBO')
try:
# Generate a refresh token from the authorization code
refresh_token = generate_qbo_refresh_token(authorization_code, redirect_uri)
return connect_qbo_oauth(refresh_token, realm_id, kwargs['workspace_id'])
except (qbo_exc.UnauthorizedClientError, qbo_exc.NotFoundClientError, qbo_exc.WrongParamsError, qbo_exc.InternalServerError) as e:
print('Something went wrong', e.__dict__)
logger.info('Invalid/Expired Authorization Code or QBO application not found - %s', {'error': e.response})
return Response({'message': 'Invalid/Expired Authorization Code or QBO application not found'}, status=status.HTTP_401_UNAUTHORIZED)

Expand Down
Empty file added fyle_rest_auth/__init__.py
Empty file.
9 changes: 9 additions & 0 deletions fyle_rest_auth/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
"""
Registering models in Django Admin
"""
from django.contrib import admin

from .models import AuthToken


admin.site.register(AuthToken)
5 changes: 5 additions & 0 deletions fyle_rest_auth/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from django.apps import AppConfig


class FyleRestAuthConfig(AppConfig):
name = 'fyle_rest_auth'
102 changes: 102 additions & 0 deletions fyle_rest_auth/authentication.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
from typing import Dict

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

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

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

User = get_user_model()
auth = AuthUtils()


class FyleJWTAuthentication(BaseAuthentication):
"""
Fyle Authentication class
"""
def authenticate(self, request):
"""
Authentication function
"""
access_token_string = self.get_header(request)

user = self.validate_token(
access_token_string=access_token_string,
origin_address=auth.get_origin_address(request)
)

try:
user = User.objects.get(email=user['email'], user_id=user['user_id'])
print('user',user)
token = AuthToken.objects.get(user=user)
print('success', token)
except User.DoesNotExist:
raise ValidationError('User not found for this token')
except AuthToken.DoesNotExist:
raise ValidationError('Login details not found for the user')

return user, None

@staticmethod
def get_header(request) -> str:
"""
Extracts the header containing the JSON web token from the given
request.
"""
header = request.META.get('HTTP_AUTHORIZATION')

return header

@staticmethod
def validate_token(access_token_string: str, origin_address: str) -> Dict:
"""
Validate the access token
:param origin_address:
:param access_token_string:
:return:
"""
if not access_token_string:
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 ValidationError('Invalid access token structure')

print('access_token_tokenizer', access_token_tokenizer)
unique_key_generator = access_token_tokenizer[1].split('.')
email_unique_key = 'email_{0}'.format(unique_key_generator[2])
user_unique_key = 'user_{0}'.format(unique_key_generator[2])

email = cache.get(email_unique_key)
user = cache.get(user_unique_key)

if not (email and user):
cache.delete_many([email_unique_key, user_unique_key])

try:
employee_info = get_fyle_admin(access_token_string.split(' ')[1], origin_address)
except Exception:
raise AuthenticationFailed('Invalid access token')

cache.set(email_unique_key, employee_info['data']['user']['email'])
cache.set(user_unique_key, employee_info['data']['user']['id'])

print('employee_info', employee_info)
return {
'email': employee_info['data']['user']['email'],
'user_id': employee_info['data']['user']['id']
}

elif email and user:
print('email', email)
print('user', user)
return {
'email': email,
'user_id': user
}

raise AuthenticationFailed('Invalid access token')
189 changes: 189 additions & 0 deletions fyle_rest_auth/helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
import logging
import traceback
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 django_q.tasks import async_task

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

auth = AuthUtils()

logger = logging.getLogger(__name__)
logger.level = logging.INFO


def validate_code_and_login(request):
authorization_code = request.data.get('code')
try:
if not authorization_code:
raise ValidationError('authorization code not found')

tokens = auth.generate_fyle_refresh_token(authorization_code=authorization_code)

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['data']['user']['id'],
email=employee_info['data']['user']['email']
)

AuthToken.objects.update_or_create(
user=user,
defaults={
'refresh_token': tokens['refresh_token']
}
)

serializer = import_string(settings.FYLE_REST_AUTH_SERIALIZERS['USER_DETAILS_SERIALIZER'])
tokens['user'] = serializer(user).data
tokens['user']['full_name'] = employee_info['data']['user']['full_name']
tokens['user']['org_id'] = employee_info['data']['org']['id']
tokens['user']['org_name'] = employee_info['data']['org']['name']

# Update Fyle Credentials with latest healthy token
if 'async_update_user' in settings.FYLE_REST_AUTH_SETTINGS \
and settings.FYLE_REST_AUTH_SETTINGS['async_update_user']:
async_task(
'apps.workspaces.tasks.async_update_fyle_credentials',
employee_info['data']['org']['id'], tokens['refresh_token']
)

return tokens

except ValidationError as error:
logger.info(error)
raise

except Exception as error:
logger.error(traceback.format_exc())
raise ValidationError(error)


def validate_refresh_token_and_login(request):
"""
Takes refresh_token from payload
GET Fyle Admin info
Get Or Create User
Saves AuthToken
Return Tokens
"""
refresh_token = request.data.get('refresh_token')
try:
if not refresh_token:
raise ValidationError('refresh token not found')

tokens = auth.refresh_access_token(refresh_token)

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['data']['user']['id'],
email=employee_info['data']['user']['email']
)

AuthToken.objects.update_or_create(
user=user,
defaults={
'refresh_token': refresh_token
}
)

serializer = import_string(settings.FYLE_REST_AUTH_SERIALIZERS['USER_DETAILS_SERIALIZER'])
tokens['user'] = serializer(user).data
tokens['user']['full_name'] = employee_info['data']['user']['full_name']
tokens['user']['org_id'] = employee_info['data']['org']['id']
tokens['user']['org_name'] = employee_info['data']['org']['name']

# Update Fyle Credentials with latest healthy token
if 'async_update_user' in settings.FYLE_REST_AUTH_SETTINGS \
and settings.FYLE_REST_AUTH_SETTINGS['async_update_user']:
async_task(
'apps.workspaces.tasks.async_update_fyle_credentials',
employee_info['data']['org']['id'], tokens['refresh_token']
)

return tokens

except ValidationError as error:
logger.info(error)
raise

except Exception as error:
logger.error(traceback.format_exc())
raise ValidationError(error)


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

tokens = auth.refresh_access_token(refresh_token)

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

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

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

auth_token = AuthToken.objects.get(user=user)
auth_token.refresh_token = refresh_token
auth_token.save()

serializer = import_string(settings.FYLE_REST_AUTH_SERIALIZERS['USER_DETAILS_SERIALIZER'])
tokens['user'] = serializer(user).data
tokens['refresh_token'] = refresh_token

return tokens

except ValidationError as error:
logger.info(error)
raise

except Exception as error:
logger.error(traceback.format_exc())
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'] or \
('FYLE_MODULE' in settings.FYLE_REST_AUTH_SERIALIZERS and \
settings.FYLE_REST_AUTH_SERIALIZERS['FYLE_MODULE'] == 'PARTNER_DASHBOARD'):
return employee_detail
else:
raise ValidationError('User is not an admin')
26 changes: 26 additions & 0 deletions fyle_rest_auth/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Generated by Django 3.0.1 on 2020-01-01 11:15

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

initial = True

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]

operations = [
migrations.CreateModel(
name='AuthTokens',
fields=[
('id', models.AutoField(primary_key=True, serialize=False)),
('refresh_token', models.TextField(help_text='Fyle refresh token')),
('access_token', models.TextField(help_text='Fyle access token')),
('user', models.ForeignKey(help_text='User table relation', on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
]
21 changes: 21 additions & 0 deletions fyle_rest_auth/migrations/0002_auto_20200101_1205.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Generated by Django 3.0.1 on 2020-01-01 12:05

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('fyle_rest_auth', '0001_initial'),
]

operations = [
migrations.AlterField(
model_name='authtokens',
name='user',
field=models.OneToOneField(help_text='User table relation', on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
),
]
Loading
Loading