mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
Initial work on SSO support (WIP)
This commit is contained in:
@ -102,6 +102,14 @@ PyYAML
|
||||
# https://github.com/andymccurdy/redis-py
|
||||
redis
|
||||
|
||||
# Social authentication framework
|
||||
# https://github.com/python-social-auth/social-core
|
||||
social-auth-core[all]
|
||||
|
||||
# Django app for social-auth-core
|
||||
# https://github.com/python-social-auth/social-app-django
|
||||
social-auth-app-django
|
||||
|
||||
# SVG image rendering (used for rack elevations)
|
||||
# https://github.com/mozman/svgwrite
|
||||
svgwrite
|
||||
|
@ -8,7 +8,6 @@ from django.contrib import auth
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.db import ProgrammingError
|
||||
from django.http import Http404, HttpResponseRedirect
|
||||
from django.urls import reverse
|
||||
|
||||
from extras.context_managers import change_logging
|
||||
from netbox.config import clear_config
|
||||
@ -20,23 +19,15 @@ class LoginRequiredMiddleware:
|
||||
"""
|
||||
If LOGIN_REQUIRED is True, redirect all non-authenticated users to the login page.
|
||||
"""
|
||||
|
||||
def __init__(self, get_response):
|
||||
self.get_response = get_response
|
||||
|
||||
def __call__(self, request):
|
||||
# Redirect unauthenticated requests (except those exempted) to the login page if LOGIN_REQUIRED is true
|
||||
if settings.LOGIN_REQUIRED and not request.user.is_authenticated:
|
||||
# Determine exempt paths
|
||||
exempt_paths = [
|
||||
reverse('api-root'),
|
||||
reverse('graphql'),
|
||||
]
|
||||
if settings.METRICS_ENABLED:
|
||||
exempt_paths.append(reverse('prometheus-django-metrics'))
|
||||
|
||||
# Redirect unauthenticated requests
|
||||
if not request.path_info.startswith(tuple(exempt_paths)) and request.path_info != settings.LOGIN_URL:
|
||||
if not request.path_info.startswith(settings.EXEMPT_PATHS):
|
||||
login_url = f'{settings.LOGIN_URL}?next={parse.quote(request.get_full_path_info())}'
|
||||
return HttpResponseRedirect(login_url)
|
||||
|
||||
|
@ -305,6 +305,7 @@ INSTALLED_APPS = [
|
||||
'graphene_django',
|
||||
'mptt',
|
||||
'rest_framework',
|
||||
'social_django',
|
||||
'taggit',
|
||||
'timezone_field',
|
||||
'circuits',
|
||||
@ -400,7 +401,8 @@ MESSAGE_TAGS = {
|
||||
}
|
||||
|
||||
# Authentication URLs
|
||||
LOGIN_URL = '/{}login/'.format(BASE_PATH)
|
||||
LOGIN_URL = f'/{BASE_PATH}login/'
|
||||
LOGIN_REDIRECT_URL = f'/{BASE_PATH}'
|
||||
|
||||
CSRF_TRUSTED_ORIGINS = ALLOWED_HOSTS
|
||||
|
||||
@ -414,6 +416,27 @@ EXEMPT_EXCLUDE_MODELS = (
|
||||
('users', 'objectpermission'),
|
||||
)
|
||||
|
||||
# All URLs starting with a string listed here are exempt from login enforcement
|
||||
EXEMPT_PATHS = (
|
||||
f'/{BASE_PATH}api/',
|
||||
f'/{BASE_PATH}graphql/',
|
||||
f'/{BASE_PATH}login/',
|
||||
f'/{BASE_PATH}oauth/',
|
||||
f'/{BASE_PATH}metrics/',
|
||||
)
|
||||
|
||||
|
||||
#
|
||||
# Django social auth
|
||||
#
|
||||
|
||||
# Load all SOCIAL_AUTH_* settings from the user configuration
|
||||
for param in dir(configuration):
|
||||
if param.startswith('SOCIAL_AUTH_'):
|
||||
globals()[param] = getattr(configuration, param)
|
||||
|
||||
SOCIAL_AUTH_JSONFIELD_ENABLED = True
|
||||
|
||||
|
||||
#
|
||||
# Django Prometheus
|
||||
|
@ -39,6 +39,7 @@ _patterns = [
|
||||
# Login/logout
|
||||
path('login/', LoginView.as_view(), name='login'),
|
||||
path('logout/', LogoutView.as_view(), name='logout'),
|
||||
path('oauth/', include('social_django.urls', namespace='social')),
|
||||
|
||||
# Apps
|
||||
path('circuits/', include('circuits.urls')),
|
||||
|
@ -39,6 +39,14 @@
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{# TODO: Improve the design & layout #}
|
||||
{% if auth_backends %}
|
||||
<h6 class="mt-4">Or use an SSO provider:</h6>
|
||||
{% for name, backend in auth_backends.items %}
|
||||
<h4><a href="{% url 'social:begin' backend=name %}" class="my-2">{{ name }}</a></h4>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
{# Login form errors #}
|
||||
{% if form.non_field_errors %}
|
||||
<div class="alert alert-danger" role="alert">
|
||||
|
@ -1,5 +1,6 @@
|
||||
import logging
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth import login as auth_login, logout as auth_logout, update_session_auth_hash
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
@ -12,6 +13,7 @@ from django.utils.decorators import method_decorator
|
||||
from django.utils.http import is_safe_url
|
||||
from django.views.decorators.debug import sensitive_post_parameters
|
||||
from django.views.generic import View
|
||||
from social_core.backends.utils import load_backends
|
||||
|
||||
from netbox.config import get_config
|
||||
from utilities.forms import ConfirmationForm
|
||||
@ -42,6 +44,7 @@ class LoginView(View):
|
||||
|
||||
return render(request, self.template_name, {
|
||||
'form': form,
|
||||
'auth_backends': load_backends(settings.AUTHENTICATION_BACKENDS),
|
||||
})
|
||||
|
||||
def post(self, request):
|
||||
@ -69,13 +72,14 @@ class LoginView(View):
|
||||
|
||||
return render(request, self.template_name, {
|
||||
'form': form,
|
||||
'auth_backends': load_backends(settings.AUTHENTICATION_BACKENDS),
|
||||
})
|
||||
|
||||
def redirect_to_next(self, request, logger):
|
||||
if request.method == "POST":
|
||||
redirect_to = request.POST.get('next', reverse('home'))
|
||||
redirect_to = request.POST.get('next', settings.LOGIN_REDIRECT_URL)
|
||||
else:
|
||||
redirect_to = request.GET.get('next', reverse('home'))
|
||||
redirect_to = request.GET.get('next', settings.LOGIN_REDIRECT_URL)
|
||||
|
||||
if redirect_to and not is_safe_url(url=redirect_to, allowed_hosts=request.get_host()):
|
||||
logger.warning(f"Ignoring unsafe 'next' URL passed to login form: {redirect_to}")
|
||||
|
@ -23,6 +23,8 @@ netaddr==0.8.0
|
||||
Pillow==8.4.0
|
||||
psycopg2-binary==2.9.1
|
||||
PyYAML==6.0
|
||||
social-auth-app-django==5.0.0
|
||||
social-auth-core==4.1.0
|
||||
svgwrite==1.4.1
|
||||
tablib==3.0.0
|
||||
|
||||
|
Reference in New Issue
Block a user