1
0
mirror of https://github.com/netbox-community/netbox.git synced 2024-05-10 07:54:54 +00:00

294 lines
9.3 KiB
Python
Raw Normal View History

import logging
2021-10-29 17:06:14 -04:00
from django.conf import settings
2016-03-01 11:23:03 -05:00
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
from django.contrib.auth.models import update_last_login
from django.contrib.auth.signals import user_logged_in
from django.http import HttpResponseRedirect
2017-03-08 11:34:47 -05:00
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse
2017-05-19 15:47:19 -04:00
from django.utils.decorators import method_decorator
2016-03-01 11:23:03 -05:00
from django.utils.http import is_safe_url
from django.views.decorators.debug import sensitive_post_parameters
from django.views.generic import View
2021-10-29 17:06:14 -04:00
from social_core.backends.utils import load_backends
2016-03-01 11:23:03 -05:00
from extras.models import ObjectChange
from extras.tables import ObjectChangeTable
2021-10-26 13:41:56 -04:00
from netbox.config import get_config
2017-03-08 11:34:47 -05:00
from utilities.forms import ConfirmationForm
from .forms import LoginForm, PasswordChangeForm, TokenForm
from .models import Token
2016-03-01 11:23:03 -05:00
#
# Login/logout
#
2017-05-19 15:47:19 -04:00
class LoginView(View):
"""
Perform user authentication via the web UI.
"""
2017-05-19 15:47:19 -04:00
template_name = 'login.html'
2016-03-01 11:23:03 -05:00
@method_decorator(sensitive_post_parameters('password'))
def dispatch(self, *args, **kwargs):
return super().dispatch(*args, **kwargs)
2017-05-19 15:47:19 -04:00
def get(self, request):
form = LoginForm(request)
2020-09-04 16:09:05 -04:00
if request.user.is_authenticated:
logger = logging.getLogger('netbox.auth.login')
return self.redirect_to_next(request, logger)
2017-05-19 15:47:19 -04:00
return render(request, self.template_name, {
'form': form,
2021-10-29 17:06:14 -04:00
'auth_backends': load_backends(settings.AUTHENTICATION_BACKENDS),
2017-05-19 15:47:19 -04:00
})
def post(self, request):
logger = logging.getLogger('netbox.auth.login')
2016-03-01 11:23:03 -05:00
form = LoginForm(request, data=request.POST)
2016-03-01 11:23:03 -05:00
if form.is_valid():
logger.debug("Login form validation was successful")
2016-03-01 11:23:03 -05:00
# If maintenance mode is enabled, assume the database is read-only, and disable updating the user's
# last_login time upon authentication.
2021-10-26 13:41:56 -04:00
if get_config().MAINTENANCE_MODE:
logger.warning("Maintenance mode enabled: disabling update of most recent login time")
user_logged_in.disconnect(update_last_login, dispatch_uid='update_last_login')
2016-03-01 11:23:03 -05:00
# Authenticate user
auth_login(request, form.get_user())
logger.info(f"User {request.user} successfully authenticated")
2017-05-24 11:33:11 -04:00
messages.info(request, "Logged in as {}.".format(request.user))
2016-03-01 11:23:03 -05:00
2020-09-04 16:09:05 -04:00
return self.redirect_to_next(request, logger)
2016-03-01 11:23:03 -05:00
else:
logger.debug("Login form validation failed")
2017-05-19 15:47:19 -04:00
return render(request, self.template_name, {
'form': form,
2021-10-29 17:06:14 -04:00
'auth_backends': load_backends(settings.AUTHENTICATION_BACKENDS),
2017-05-19 15:47:19 -04:00
})
2016-03-01 11:23:03 -05:00
2020-09-04 16:09:05 -04:00
def redirect_to_next(self, request, logger):
if request.method == "POST":
2021-10-29 17:06:14 -04:00
redirect_to = request.POST.get('next', settings.LOGIN_REDIRECT_URL)
2020-09-04 16:09:05 -04:00
else:
2021-10-29 17:06:14 -04:00
redirect_to = request.GET.get('next', settings.LOGIN_REDIRECT_URL)
2020-09-04 16:09:05 -04:00
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}")
redirect_to = reverse('home')
logger.debug(f"Redirecting user to {redirect_to}")
return HttpResponseRedirect(redirect_to)
2016-03-01 11:23:03 -05:00
2017-05-19 15:47:19 -04:00
class LogoutView(View):
"""
Deauthenticate a web user.
"""
2021-04-25 20:11:46 -07:00
2017-05-19 15:47:19 -04:00
def get(self, request):
logger = logging.getLogger('netbox.auth.logout')
# Log out the user
username = request.user
2017-05-19 15:47:19 -04:00
auth_logout(request)
logger.info(f"User {username} has logged out")
2017-05-24 11:33:11 -04:00
messages.info(request, "You have logged out.")
2016-03-01 11:23:03 -05:00
# Delete session key cookie (if set) upon logout
response = HttpResponseRedirect(reverse('home'))
response.delete_cookie('session_key')
return response
2016-03-01 11:23:03 -05:00
#
# User profiles
#
2019-04-11 17:27:38 -04:00
class ProfileView(LoginRequiredMixin, View):
2017-05-19 15:47:19 -04:00
template_name = 'users/profile.html'
2016-03-01 11:23:03 -05:00
2017-05-19 15:47:19 -04:00
def get(self, request):
# Compile changelog table
changelog = ObjectChange.objects.restrict(request.user, 'view').filter(user=request.user).prefetch_related(
'changed_object_type'
)[:20]
changelog_table = ObjectChangeTable(changelog)
2017-05-19 15:47:19 -04:00
return render(request, self.template_name, {
'changelog_table': changelog_table,
2017-05-19 15:47:19 -04:00
'active_tab': 'profile',
})
2016-03-01 11:23:03 -05:00
class UserConfigView(LoginRequiredMixin, View):
template_name = 'users/preferences.html'
def get(self, request):
return render(request, self.template_name, {
'preferences': request.user.config.all(),
'active_tab': 'preferences',
})
def post(self, request):
userconfig = request.user.config
data = userconfig.all()
# Delete selected preferences
2021-04-25 20:11:46 -07:00
if "_delete" in request.POST:
for key in request.POST.getlist('pk'):
if key in data:
userconfig.clear(key)
# Update specific values
elif "_update" in request.POST:
for key in request.POST:
2021-04-26 00:32:26 -07:00
if not key.startswith('_') and not key.startswith('csrf'):
2021-04-25 20:11:46 -07:00
for value in request.POST.getlist(key):
userconfig.set(key, value)
userconfig.save()
messages.success(request, "Your preferences have been updated.")
return redirect('user:preferences')
2019-04-11 17:27:38 -04:00
class ChangePasswordView(LoginRequiredMixin, View):
2021-08-24 15:24:03 -04:00
template_name = 'users/password.html'
2017-05-19 15:47:19 -04:00
def get(self, request):
# LDAP users cannot change their password here
2019-12-11 07:03:39 +00:00
if getattr(request.user, 'ldap_username', None):
messages.warning(request, "LDAP-authenticated user credentials cannot be changed within NetBox.")
return redirect('user:profile')
2017-05-19 15:47:19 -04:00
form = PasswordChangeForm(user=request.user)
return render(request, self.template_name, {
'form': form,
2021-08-24 15:24:03 -04:00
'active_tab': 'password',
2017-05-19 15:47:19 -04:00
})
2016-03-01 11:23:03 -05:00
2017-05-19 15:47:19 -04:00
def post(self, request):
2016-03-01 11:23:03 -05:00
form = PasswordChangeForm(user=request.user, data=request.POST)
if form.is_valid():
form.save()
update_session_auth_hash(request, form.user)
2017-05-24 11:33:11 -04:00
messages.success(request, "Your password has been changed successfully.")
2017-03-14 12:36:44 -04:00
return redirect('user:profile')
2016-03-01 11:23:03 -05:00
2017-05-19 15:47:19 -04:00
return render(request, self.template_name, {
'form': form,
'active_tab': 'change_password',
})
2016-03-01 11:23:03 -05:00
#
# API tokens
#
2017-03-08 11:34:47 -05:00
class TokenListView(LoginRequiredMixin, View):
def get(self, request):
tokens = Token.objects.filter(user=request.user)
return render(request, 'users/api_tokens.html', {
'tokens': tokens,
2021-08-24 15:24:03 -04:00
'active_tab': 'api-tokens',
})
2017-03-08 11:34:47 -05:00
class TokenEditView(LoginRequiredMixin, View):
def get(self, request, pk=None):
if pk:
2017-03-08 11:34:47 -05:00
token = get_object_or_404(Token.objects.filter(user=request.user), pk=pk)
else:
token = Token(user=request.user)
form = TokenForm(instance=token)
2020-11-11 16:58:29 -05:00
return render(request, 'generic/object_edit.html', {
2017-03-08 11:34:47 -05:00
'obj': token,
'obj_type': token._meta.verbose_name,
'form': form,
2017-03-14 12:59:10 -04:00
'return_url': reverse('user:token_list'),
2017-03-08 11:34:47 -05:00
})
def post(self, request, pk=None):
if pk:
2017-03-08 11:34:47 -05:00
token = get_object_or_404(Token.objects.filter(user=request.user), pk=pk)
form = TokenForm(request.POST, instance=token)
else:
token = Token(user=request.user)
2017-03-08 11:34:47 -05:00
form = TokenForm(request.POST)
if form.is_valid():
token = form.save(commit=False)
token.user = request.user
token.save()
msg = f"Modified token {token}" if pk else f"Created token {token}"
2017-03-08 11:34:47 -05:00
messages.success(request, msg)
if '_addanother' in request.POST:
return redirect(request.path)
else:
return redirect('user:token_list')
2017-03-08 11:34:47 -05:00
2020-11-11 16:58:29 -05:00
return render(request, 'generic/object_edit.html', {
'obj': token,
'obj_type': token._meta.verbose_name,
'form': form,
'return_url': reverse('user:token_list'),
})
2017-03-08 11:34:47 -05:00
class TokenDeleteView(LoginRequiredMixin, View):
2017-03-08 11:34:47 -05:00
def get(self, request, pk):
token = get_object_or_404(Token.objects.filter(user=request.user), pk=pk)
initial_data = {
2017-03-14 12:59:10 -04:00
'return_url': reverse('user:token_list'),
2017-03-08 11:34:47 -05:00
}
form = ConfirmationForm(initial=initial_data)
2020-11-11 16:58:29 -05:00
return render(request, 'generic/object_delete.html', {
2017-03-08 11:34:47 -05:00
'obj': token,
'obj_type': token._meta.verbose_name,
'form': form,
2017-03-14 12:59:10 -04:00
'return_url': reverse('user:token_list'),
2017-03-08 11:34:47 -05:00
})
def post(self, request, pk):
token = get_object_or_404(Token.objects.filter(user=request.user), pk=pk)
form = ConfirmationForm(request.POST)
if form.is_valid():
token.delete()
messages.success(request, "Token deleted")
2017-03-14 12:59:10 -04:00
return redirect('user:token_list')
2017-03-08 11:34:47 -05:00
2020-11-11 16:58:29 -05:00
return render(request, 'generic/object_delete.html', {
2017-03-08 11:34:47 -05:00
'obj': token,
'obj_type': token._meta.verbose_name,
'form': form,
2017-03-14 12:59:10 -04:00
'return_url': reverse('user:token_list'),
2017-03-08 11:34:47 -05:00
})