Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
…e_py into develop
  • Loading branch information
hirensoni913 committed Jun 5, 2024
2 parents 63943cb + 9e1d902 commit 6512cf7
Show file tree
Hide file tree
Showing 19 changed files with 330 additions and 98 deletions.
11 changes: 11 additions & 0 deletions core/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ def from_db_value(self, value, expression, connection):
from core import datetime
return datetime.date.from_ad_date(py_datetime.date(value.year, value.month, value.day))

def get_prep_value(self, value):
if hasattr(value, 'to_ad_date'):
value = value.to_ad_date()
return models.DateTimeField().get_prep_value(value)


class DateTimeField(models.DateTimeField):
description = "Calendar-aware DateTime field"
Expand All @@ -20,3 +25,9 @@ def from_db_value(self, value, expression, connection):
return None
from core import datetime
return datetime.datetime.from_ad_datetime(value)

def get_prep_value(self, value):
if hasattr(value, 'to_ad_datetime'):
value = value.to_ad_datetime()
return models.DateTimeField().get_prep_value(value)

23 changes: 9 additions & 14 deletions core/jwt.py
Original file line number Diff line number Diff line change
@@ -1,36 +1,37 @@
# from graphql_auth.backends import GraphQLAuthBackend
# from django.utils.deprecation import MiddlewareMixin
import jwt
from graphql_jwt.settings import jwt_settings
from graphql_jwt.signals import token_issued
from django.apps import apps
from django.utils import timezone
from django.dispatch import receiver

import logging
import uuid
from datetime import datetime

logger = logging.getLogger(__file__)


@receiver(token_issued)
def on_token_issued(sender, request, user, **kwargs):
# Store the date on which the user got the auth token
user.last_login = timezone.now()
user.save()
pass


def jwt_encode_user_key(payload, context=None):
payload['jti'] = str(uuid.uuid4())
payload['nbf'] = datetime.utcnow()

token = jwt.encode(
payload,
get_jwt_key(encode=True, context=context, payload=payload),
jwt_settings.JWT_ALGORITHM,
algorithm=jwt_settings.JWT_ALGORITHM,
)
# JWT module after 1.7 does the encoding, introducing some conflicts in graphql-jwt, let's support both
if isinstance(token, bytes):
token = token.decode("utf-8")
return token


def jwt_decode_user_key(token, context=None):
# First decode the token without validating it, so we can extract the username
not_validated = jwt.decode(
Expand Down Expand Up @@ -72,7 +73,6 @@ def jwt_decode_user_key(token, context=None):
algorithms=[jwt_settings.JWT_ALGORITHM],
)


def get_jwt_key(encode=True, context=None, payload=None):
user_key = extract_private_key_from_context(context)
if user_key is None and payload is not None:
Expand All @@ -81,13 +81,9 @@ def get_jwt_key(encode=True, context=None, payload=None):
return user_key

if encode:
if hasattr(jwt_settings, "JWT_PRIVATE_KEY"):
return jwt_settings.JWT_PRIVATE_KEY
return getattr(jwt_settings, "JWT_PRIVATE_KEY", jwt_settings.JWT_SECRET_KEY)
else:
if hasattr(jwt_settings, "JWT_PUBLIC_KEY"):
return jwt_settings.JWT_PUBLIC_KEY
return jwt_settings.JWT_SECRET_KEY

return getattr(jwt_settings, "JWT_PUBLIC_KEY", jwt_settings.JWT_SECRET_KEY)

def extract_private_key_from_payload(payload):
# Get user private key from payload. This covers the refresh token mutation
Expand All @@ -96,7 +92,6 @@ def extract_private_key_from_payload(payload):
if "username" in payload:
return User.objects.get(username=payload["username"]).private_key


def extract_private_key_from_context(context):
if context and context.user and hasattr(context.user, "private_key"):
return context.user.private_key
Expand Down
21 changes: 21 additions & 0 deletions core/jwt_authentication.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
from .jwt import jwt_decode_user_key

from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import Throttled
from rest_framework import exceptions
from graphql_jwt.utils import get_credentials
from graphql_jwt.exceptions import JSONWebTokenError
from graphql_jwt.shortcuts import get_user_by_token
from core.apps import CoreConfig
from django.conf import settings
from django_ratelimit.core import is_ratelimited

from datetime import date
import jwt
Expand All @@ -21,6 +24,7 @@ class to obtain token from header if it is provided
"""

def authenticate(self, request):
self.check_rate_limit(request)
token = get_credentials(request)
if token:
# Do not pass context to avoid to try to get user from request to get his private key.
Expand All @@ -40,3 +44,20 @@ def authenticate(self, request):

def enforce_csrf(self, request):
return # To not perform the csrf during checking auth header

@staticmethod
def check_rate_limit(request) -> None:
group = settings.RATELIMIT_GROUP
key = settings.RATELIMIT_KEY
rate = settings.RATELIMIT_RATE

if is_ratelimited(
request=request,
group=group,
fn=None,
key=key,
rate=rate,
method=is_ratelimited.ALL,
increment=True
):
raise Throttled(detail='Rate limit exceeded')
45 changes: 45 additions & 0 deletions core/middleware.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
from django.utils.timezone import now
from django_ratelimit.core import is_ratelimited
from rest_framework.exceptions import JsonResponse
from django.conf import settings


class DefaultAxesAttributesMiddleware:
Expand All @@ -15,3 +18,45 @@ def __call__(self, request):
request.axes_attempt_time = now()

return self.get_response(request)


class SecurityHeadersMiddleware:
def __init__(self, get_response):
self.get_response = get_response

def __call__(self, request):
response = self.get_response(request)

if settings.MODE == "PROD":
response["Strict-Transport-Security"] = "max-age=63072000; includeSubDomains"

response["Content-Security-Policy"] = "default-src 'self';"
response["X-Frame-Options"] = "DENY"
response["X-Content-Type-Options"] = "nosniff"
response["Referrer-Policy"] = "no-referrer"
response["Permissions-Policy"] = "geolocation=(), microphone=()"

return response


class GraphQLRateLimitMiddleware:
def __init__(self, get_response):
self.get_response = get_response

def __call__(self, request):
group = settings.RATELIMIT_GROUP
key = settings.RATELIMIT_KEY
rate = settings.RATELIMIT_RATE
if request.path == '/api/graphql':
rate_limited = is_ratelimited(
request=request,
group=group,
key=key,
rate=rate,
method=is_ratelimited.ALL,
increment=True
)
if rate_limited:
return JsonResponse({'detail': 'Rate limit exceeded'}, status=429)
response = self.get_response(request)
return response
16 changes: 8 additions & 8 deletions core/models/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
from django.utils.crypto import salted_hmac
from graphql import ResolveInfo
import core
from core.utils import validate_password
# from core.utils import validate_password
from django.contrib.auth.password_validation import validate_password
#from core.datetimes.ad_datetime import datetime as py_datetime
from django.conf import settings

Expand Down Expand Up @@ -232,13 +233,14 @@ def stored_password(self, value):

@property
def is_staff(self):
return False
return self.is_superuser


@property
def is_superuser(self):
if self.user and self.user.t_user:
return self.user.t_user.is_superuser
return False
return self.is_imis_admin



@property
def rights(self):
Expand Down Expand Up @@ -412,9 +414,7 @@ def is_staff(self):

@property
def is_superuser(self):
if self.user and self.user.t_user:
return self.user.t_user.is_superuser
return False
return self._u.is_superuser

@property
def is_imis_admin(self):
Expand Down
7 changes: 3 additions & 4 deletions core/models/versioned_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from ..fields import DateTimeField
from ..utils import filter_validity
import logging
import datetime

logger = logging.getLogger(__name__)

Expand All @@ -21,16 +22,14 @@ def save_history(self, **kwargs):
histo.id = None
if hasattr(histo, "uuid"):
setattr(histo, "uuid", uuid.uuid4())
from core import datetime
histo.validity_to = datetime.datetime.now()
histo.validity_to = py_datetime.now()
histo.legacy_id = self.id
histo.save()
return histo.id

def delete_history(self, **kwargs):
self.save_history()
from core import datetime
now = datetime.datetime.now()
now = py_datetime.now()
self.validity_from = now
self.validity_to = now
self.save()
Expand Down
Loading

0 comments on commit 6512cf7

Please sign in to comment.