Skip to content

Commit

Permalink
feat[bckend-proxy-middleware]: Added Nginx as a reverse proxy for API…
Browse files Browse the repository at this point in the history
… requests and implemented a middleware to filter API requests based on user roles.
  • Loading branch information
shikharpa committed Apr 8, 2024
1 parent d4b819d commit f80c38e
Show file tree
Hide file tree
Showing 7 changed files with 89 additions and 16 deletions.
10 changes: 8 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ services:
poetry run python manage.py runserver 0.0.0.0:8000
volumes:
- ./services/api/:/usr/src/app/
ports:
- 8000:8000
environment:
- SECRET_KEY=kalvisquad
- DATABASE_NAME=kalvi
Expand Down Expand Up @@ -40,5 +38,13 @@ services:
- POSTGRES_DB=kalvi
redis:
image: redis:alpine
proxy:
container_name: proxy
build:
context: ./nginx
dockerfile: Dockerfile
restart: always
ports:
- 80:80
volumes:
postgres_data:
8 changes: 8 additions & 0 deletions nginx/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
FROM nginx:stable-alpine
RUN apk add --no-cache openssl gettext
RUN rm /etc/nginx/conf.d/default.conf
COPY nginx.conf.dev /etc/nginx/nginx.conf.template
COPY ./env.sh /docker-entrypoint.sh
RUN chmod +x /docker-entrypoint.sh
# Update all environment variables
CMD ["/docker-entrypoint.sh"]
4 changes: 4 additions & 0 deletions nginx/env.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/sh

envsubst < /etc/nginx/nginx.conf.template > /etc/nginx/nginx.conf
exec nginx -g 'daemon off;'
21 changes: 21 additions & 0 deletions nginx/nginx.conf.dev
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
events {
}

http {
sendfile on;

server {
listen 80;
root /www/data/;
access_log /var/log/nginx/access.log;

add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Permissions-Policy "interest-cohort=()" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

location /api/ {
proxy_pass http://api:8000/api/;
}
}
}
28 changes: 14 additions & 14 deletions services/api/kalvi/api/urls/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,69 +5,69 @@

urlpatterns = [
path(
"sign-up/",
"common/sign-up/",
SignUpEndPoint.as_view(),
name="kalvi-sign-up",
),
path(
"sign-in/",
"common/sign-in/",
SignInEndPoint.as_view(),
name="kalvi-sign-in",
),
path(
"user/",
"common/user/",
UserProfileView.as_view(),
name="user-detail",
),
path(
"update-password/",
"common/update-password/",
UserChangePasswordView.as_view(),
name="password-update",
),
path(
"send-reset-password-email/",
"common/send-reset-password-email/",
SendPasswordResetEmailView.as_view(),
name="send-reset-email",
),
path(
"reset-password/<str:uid>/<str:token>/",
"common/reset-password/<str:uid>/<str:token>/",
UserPasswordResetView.as_view(),
name="reset-password-through-mail",
),
path(
"google/",
"common/google/auth/",
GoogleSocialAuthView.as_view(),
name="google-sign-in"
),
path(
"github/",
"common/github/auth/",
GithubSocialAuthView.as_view(),
name="github-sign-in"
),
path('token/refresh/',
path('common/token/refresh/',
TokenRefreshView.as_view(),
name='token_refresh'
),
path("sign-out/",
path("common/sign-out/",
SignOutEndpoint.as_view(),
name="sign-out"
),
path(
"magic-generate/",
"common/magic-generate/",
MagicGenerateEndpoint.as_view(),
name="magic-generate",
),
path(
"magic-sign-in/",
"common/magic-sign-in/",
MagicSignInEndpoint.as_view(),
name="magic-sign-in"
),
path(
'profile/',
'common/profile/',
ProfileAPIView.as_view(),
name='profile'
),
path('organization-settings/',
path('admin/organization-settings/',
OrganizationSettingsAPIView.as_view(),
name='organization-settings')
]
30 changes: 30 additions & 0 deletions services/api/kalvi/middlewares/AdminUserMiddleware.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# custom_middleware.py
from rest_framework import status
from django.http import JsonResponse
from rest_framework_simplejwt.tokens import UntypedToken
from db.models import User
from rest_framework_simplejwt.exceptions import InvalidToken, TokenError

class AdminUserMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
if 'HTTP_AUTHORIZATION' in request.META:
try:
# Extract token and retrieve user object with better error handling
token = UntypedToken(request.META['HTTP_AUTHORIZATION'].split()[1])
user_id = token.payload['user_id']
except (InvalidToken, TokenError):
return JsonResponse({'error': "Invalid or expired token"}, status=status.HTTP_401_UNAUTHORIZED)
try:
user = User.objects.get(pk=user_id)
except User.DoesNotExist:
return JsonResponse({'error': "User not found"}, status=status.HTTP_403_FORBIDDEN)
is_admin = '/api/admin/' in request.path
is_user = '/api/user/' in request.path
# If the user is trying to access the wrong API, return Forbidden
if is_admin and not user.is_superuser:
return JsonResponse({'error': "Access is not allowed"}, status=status.HTTP_403_FORBIDDEN)
if is_user and user.is_superuser:
return JsonResponse({'error': "Access is not allowed"}, status=status.HTTP_403_FORBIDDEN)
return self.get_response(request)
4 changes: 4 additions & 0 deletions services/api/kalvi/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
"kalvi.middlewares.AdminUserMiddleware.AdminUserMiddleware",
]

ROOT_URLCONF = "kalvi.urls"
Expand Down Expand Up @@ -152,6 +153,9 @@
"DEFAULT_PERMISSION_CLASSES": (
"rest_framework.permissions.IsAuthenticated",
),
"DEFAULT_RENDERER_CLASSES": (
"rest_framework.renderers.JSONRenderer",
),
}

AUTH_USER_MODEL = "db.User"
Expand Down

0 comments on commit f80c38e

Please sign in to comment.