add authentication
This commit is contained in:
@@ -20,3 +20,6 @@ DRF_SECRET_KEY=
|
||||
DRF_DEBUG=
|
||||
ALLOWED_HOSTS=
|
||||
CSRF_TRUSTED_ORIGINS=
|
||||
|
||||
OIDC_RP_CLIENT_ID=
|
||||
OIDC_WELLKNOWN=
|
||||
@@ -12,6 +12,7 @@ RUN python -m pip install --no-cache-dir --upgrade -r requirements.txt
|
||||
COPY --chown=appuser:appuser marbas /app/marbas
|
||||
COPY --chown=appuser:appuser sde /app/sde
|
||||
COPY --chown=appuser:appuser api /app/api
|
||||
COPY --chown=appuser:appuser authentication /app/authentication
|
||||
|
||||
USER appuser
|
||||
CMD ["uvicorn", "marbas.asgi:application", "--host", "0.0.0.0", "--port", "8000"]
|
||||
0
authentication/__init__.py
Normal file
0
authentication/__init__.py
Normal file
3
authentication/admin.py
Normal file
3
authentication/admin.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
6
authentication/apps.py
Normal file
6
authentication/apps.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class AuthConfig(AppConfig):
|
||||
default_auto_field = "django.db.models.BigAutoField"
|
||||
name = "authentication"
|
||||
57
authentication/backends.py
Normal file
57
authentication/backends.py
Normal file
@@ -0,0 +1,57 @@
|
||||
from functools import wraps
|
||||
|
||||
from django.core.cache import cache
|
||||
from django.http import JsonResponse
|
||||
from django.contrib.auth import authenticate
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.auth.backends import ModelBackend
|
||||
|
||||
from rest_framework.exceptions import AuthenticationFailed
|
||||
|
||||
from mozilla_django_oidc.auth import OIDCAuthenticationBackend
|
||||
from mozilla_django_oidc.contrib.drf import OIDCAuthentication
|
||||
|
||||
|
||||
class CustomOIDCBackend(OIDCAuthenticationBackend):
|
||||
def get_username(self, claims):
|
||||
if 'preferred_username' in claims and not User.objects.filter(username=claims['preferred_username']).exists():
|
||||
print(claims['preferred_username'])
|
||||
return claims['preferred_username']
|
||||
return super().get_username(claims)
|
||||
|
||||
def authenticate(self, request, **kwargs):
|
||||
"""Hack to use the same auth as DRF"""
|
||||
back = OIDCAuthentication()
|
||||
try:
|
||||
u, tok = back.authenticate(request)
|
||||
except AuthenticationFailed:
|
||||
u = None
|
||||
return u
|
||||
|
||||
def get_userinfo(self, access_token, id_token, payload):
|
||||
userinfo = cache.get(f'userinfo-{access_token}')
|
||||
if userinfo is None:
|
||||
print("no cache found for userinfo-{access_token} yet.")
|
||||
userinfo = super().get_userinfo(access_token, id_token, payload)
|
||||
if userinfo:
|
||||
cache.set(f'userinfo-{access_token}', userinfo, timeout=60*60*24)
|
||||
return userinfo
|
||||
|
||||
def update_user(self, user, claims): # TODO: update groups?
|
||||
return super().update_user(user, claims)
|
||||
|
||||
def create_user(self, claims): # TODO: add groups?
|
||||
return super().create_user(claims)
|
||||
|
||||
|
||||
def login_required(func):
|
||||
@wraps(func)
|
||||
def wrapper(request, *args, **kwargs):
|
||||
if request.META.get("HTTP_AUTHORIZATION", "").startswith("Bearer"):
|
||||
if not hasattr(request, "user") or request.user.is_anonymous:
|
||||
user = authenticate(request=request)
|
||||
if not user:
|
||||
return JsonResponse({"error": "Unauthorized"}, status=401)
|
||||
request.user = request._cached_user = user
|
||||
return func(request, *args, **kwargs)
|
||||
return wrapper
|
||||
3
authentication/tests.py
Normal file
3
authentication/tests.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
9
authentication/urls.py
Normal file
9
authentication/urls.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from . import views
|
||||
from django.urls import include, path
|
||||
from rest_framework import routers
|
||||
|
||||
router = routers.DefaultRouter()
|
||||
|
||||
urlpatterns = [
|
||||
path('', include(router.urls)),
|
||||
]
|
||||
@@ -1,4 +1,3 @@
|
||||
version: '3'
|
||||
services:
|
||||
migrations:
|
||||
image: marbas:local
|
||||
@@ -12,6 +11,7 @@ services:
|
||||
- ./marbas:/app/marbas
|
||||
- ./api:/app/api
|
||||
- ./sde:/app/sde
|
||||
- ./authentication:/app/authentication
|
||||
- ./manage.py:/app/manage.py
|
||||
command: sh -c "python manage.py makemigrations && python manage.py migrate"
|
||||
depends_on:
|
||||
@@ -29,6 +29,7 @@ services:
|
||||
- ./marbas:/app/marbas
|
||||
- ./api:/app/api
|
||||
- ./sde:/app/sde
|
||||
- ./authentication:/app/authentication
|
||||
- ./manage.py:/app/manage.py
|
||||
- ./static_eve:/app/static_eve
|
||||
command: ["uvicorn", "marbas.asgi:application", "--host", "0.0.0.0", "--port", "8000", "--reload"]
|
||||
|
||||
@@ -12,6 +12,7 @@ https://docs.djangoproject.com/en/4.2/ref/settings/
|
||||
|
||||
from pathlib import Path
|
||||
import os
|
||||
import requests
|
||||
|
||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||
@@ -42,21 +43,27 @@ REST_FRAMEWORK = {
|
||||
'rest_framework.parsers.JSONParser',
|
||||
],
|
||||
'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend'],
|
||||
'DEFAULT_AUTHENTICATION_CLASSES': [
|
||||
'mozilla_django_oidc.contrib.drf.OIDCAuthentication',
|
||||
# 'rest_framework.authentication.SessionAuthentication',
|
||||
],
|
||||
}
|
||||
|
||||
INSTALLED_APPS = [
|
||||
'authentication',
|
||||
'api',
|
||||
'sde',
|
||||
'esi',
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'mozilla_django_oidc',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'django_filters',
|
||||
'health_check',
|
||||
'rest_framework'
|
||||
'rest_framework',
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
@@ -133,6 +140,12 @@ AUTH_PASSWORD_VALIDATORS = [
|
||||
},
|
||||
]
|
||||
|
||||
AUTHENTICATION_BACKENDS = [
|
||||
'authentication.backends.CustomOIDCBackend',
|
||||
'authentication.backends.EveAuthBackend',
|
||||
'django.contrib.auth.backends.ModelBackend',
|
||||
]
|
||||
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/4.2/topics/i18n/
|
||||
@@ -161,3 +174,13 @@ ESI_SSO_CLIENT_SECRET = os.getenv("ESI_SECRET_KEY")
|
||||
ESI_SSO_CALLBACK_URL = os.getenv("ESI_CALLBACK_URL")
|
||||
ESI_USER_AGENT = os.getenv("ESI_USER_AGENT")
|
||||
ESI_USER_CONTACT_EMAIL = os.getenv("ESI_USER_AGENT")
|
||||
ESI_SCOPES = ['publicData', 'esi-calendar.respond_calendar_events.v1', 'esi-calendar.read_calendar_events.v1', 'esi-location.read_location.v1', 'esi-location.read_ship_type.v1', 'esi-mail.organize_mail.v1', 'esi-mail.read_mail.v1', 'esi-mail.send_mail.v1', 'esi-skills.read_skills.v1', 'esi-skills.read_skillqueue.v1', 'esi-wallet.read_character_wallet.v1', 'esi-wallet.read_corporation_wallet.v1', 'esi-search.search_structures.v1', 'esi-clones.read_clones.v1', 'esi-characters.read_contacts.v1', 'esi-universe.read_structures.v1', 'esi-bookmarks.read_character_bookmarks.v1', 'esi-killmails.read_killmails.v1', 'esi-corporations.read_corporation_membership.v1', 'esi-assets.read_assets.v1', 'esi-planets.manage_planets.v1', 'esi-fleets.read_fleet.v1', 'esi-fleets.write_fleet.v1', 'esi-ui.open_window.v1', 'esi-ui.write_waypoint.v1', 'esi-characters.write_contacts.v1', 'esi-fittings.read_fittings.v1', 'esi-fittings.write_fittings.v1', 'esi-markets.structure_markets.v1', 'esi-corporations.read_structures.v1', 'esi-characters.read_loyalty.v1', 'esi-characters.read_opportunities.v1', 'esi-characters.read_chat_channels.v1', 'esi-characters.read_medals.v1', 'esi-characters.read_standings.v1', 'esi-characters.read_agents_research.v1', 'esi-industry.read_character_jobs.v1', 'esi-markets.read_character_orders.v1', 'esi-characters.read_blueprints.v1', 'esi-characters.read_corporation_roles.v1', 'esi-location.read_online.v1', 'esi-contracts.read_character_contracts.v1', 'esi-clones.read_implants.v1', 'esi-characters.read_fatigue.v1', 'esi-killmails.read_corporation_killmails.v1', 'esi-corporations.track_members.v1', 'esi-wallet.read_corporation_wallets.v1', 'esi-characters.read_notifications.v1', 'esi-corporations.read_divisions.v1', 'esi-corporations.read_contacts.v1', 'esi-assets.read_corporation_assets.v1', 'esi-corporations.read_titles.v1', 'esi-corporations.read_blueprints.v1', 'esi-bookmarks.read_corporation_bookmarks.v1', 'esi-contracts.read_corporation_contracts.v1', 'esi-corporations.read_standings.v1', 'esi-corporations.read_starbases.v1', 'esi-industry.read_corporation_jobs.v1', 'esi-markets.read_corporation_orders.v1', 'esi-corporations.read_container_logs.v1', 'esi-industry.read_character_mining.v1', 'esi-industry.read_corporation_mining.v1', 'esi-planets.read_customs_offices.v1', 'esi-corporations.read_facilities.v1', 'esi-corporations.read_medals.v1', 'esi-characters.read_titles.v1', 'esi-alliances.read_contacts.v1', 'esi-characters.read_fw_stats.v1', 'esi-corporations.read_fw_stats.v1', 'esi-characterstats.read.v1']
|
||||
|
||||
|
||||
OIDC_RP_CLIENT_ID = os.getenv("OIDC_RP_CLIENT_ID")
|
||||
OIDC_RP_CLIENT_SECRET = ""
|
||||
if WN := os.getenv("OIDC_WELLKNOWN"):
|
||||
oauth_conf = requests.get(WN).json()
|
||||
OIDC_OP_AUTHORIZATION_ENDPOINT = oauth_conf["authorization_endpoint"]
|
||||
OIDC_OP_TOKEN_ENDPOINT = oauth_conf["token_endpoint"]
|
||||
OIDC_OP_USER_ENDPOINT = oauth_conf["userinfo_endpoint"]
|
||||
|
||||
@@ -22,7 +22,7 @@ from django.views.generic import TemplateView
|
||||
urlpatterns = [
|
||||
path('api/', include("api.urls")),
|
||||
path('sde/', include("sde.urls")),
|
||||
path('api-auth/', include('rest_framework.urls', namespace='rest_framework')),
|
||||
path('auth/', include("authentication.urls")),
|
||||
path('sso/', include('esi.urls', namespace='esi')),
|
||||
path('openapi/', get_schema_view(
|
||||
title="marbas",
|
||||
|
||||
@@ -10,3 +10,4 @@ uritemplate
|
||||
inflection
|
||||
django-esi
|
||||
django-health-check
|
||||
mozilla-django-oidc
|
||||
Reference in New Issue
Block a user