mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-03-11 09:04:54 +00:00
Override monkey-patched LoginView from django-axes 3.0.3
- Fixes #1154 - Fixes #1138 Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org> Signed-off-by: Joseph Nuthalapati <njoseph@thoughtworks.com> Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
This commit is contained in:
parent
4e3216ce7c
commit
fc9ce8e6dd
@ -17,10 +17,6 @@
|
||||
#
|
||||
|
||||
import argparse
|
||||
import django.conf
|
||||
from django.contrib.messages import constants as message_constants
|
||||
import django.core.management
|
||||
import django.core.wsgi
|
||||
import importlib
|
||||
import logging
|
||||
import os
|
||||
@ -28,13 +24,16 @@ import stat
|
||||
import sys
|
||||
import warnings
|
||||
|
||||
import cherrypy
|
||||
import django.conf
|
||||
import django.core.management
|
||||
import django.core.wsgi
|
||||
from django.contrib.messages import constants as message_constants
|
||||
|
||||
from plinth import cfg
|
||||
from plinth import menu
|
||||
from plinth import module_loader
|
||||
from plinth import service
|
||||
from plinth import setup
|
||||
import axes
|
||||
import cherrypy
|
||||
from plinth import cfg, menu, module_loader, service, setup
|
||||
|
||||
axes.default_app_config = "plinth.axes_app_config.AppConfig"
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -47,27 +46,22 @@ def parse_arguments():
|
||||
description='Plinth web interface for FreedomBox',
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
||||
# TODO: server_dir is actually a url prefix; use a better variable name
|
||||
parser.add_argument(
|
||||
'--server_dir', default=cfg.server_dir,
|
||||
help='web server path under which to serve')
|
||||
parser.add_argument(
|
||||
'--debug', action='store_true', default=cfg.debug,
|
||||
help='enable debugging and run server *insecurely*')
|
||||
parser.add_argument('--server_dir', default=cfg.server_dir,
|
||||
help='web server path under which to serve')
|
||||
parser.add_argument('--debug', action='store_true', default=cfg.debug,
|
||||
help='enable debugging and run server *insecurely*')
|
||||
parser.add_argument(
|
||||
'--setup', default=False, nargs='*',
|
||||
help='run setup tasks on all essential modules and exit')
|
||||
parser.add_argument(
|
||||
'--setup-no-install', default=False, nargs='*',
|
||||
help='run setup tasks without installing packages and exit')
|
||||
parser.add_argument(
|
||||
'--diagnose', action='store_true', default=False,
|
||||
help='run diagnostic tests and exit')
|
||||
parser.add_argument(
|
||||
'--list-dependencies', default=False, nargs='*',
|
||||
help='list package dependencies for essential modules')
|
||||
parser.add_argument(
|
||||
'--list-modules', default=False, nargs='*',
|
||||
help='list modules')
|
||||
parser.add_argument('--diagnose', action='store_true', default=False,
|
||||
help='run diagnostic tests and exit')
|
||||
parser.add_argument('--list-dependencies', default=False, nargs='*',
|
||||
help='list package dependencies for essential modules')
|
||||
parser.add_argument('--list-modules', default=False, nargs='*',
|
||||
help='list modules')
|
||||
|
||||
global arguments
|
||||
arguments = parser.parse_args()
|
||||
@ -111,9 +105,12 @@ def setup_server():
|
||||
|
||||
static_dir = os.path.join(cfg.file_root, 'static')
|
||||
config = {
|
||||
'/': {'tools.staticdir.root': static_dir,
|
||||
'tools.staticdir.on': True,
|
||||
'tools.staticdir.dir': '.'}}
|
||||
'/': {
|
||||
'tools.staticdir.root': static_dir,
|
||||
'tools.staticdir.on': True,
|
||||
'tools.staticdir.dir': '.'
|
||||
}
|
||||
}
|
||||
cherrypy.tree.mount(None, django.conf.settings.STATIC_URL, config)
|
||||
logger.debug('Serving static directory %s on %s', static_dir,
|
||||
django.conf.settings.STATIC_URL)
|
||||
@ -121,9 +118,12 @@ def setup_server():
|
||||
js_dir = '/usr/share/javascript'
|
||||
js_url = '/javascript'
|
||||
config = {
|
||||
'/': {'tools.staticdir.root': js_dir,
|
||||
'tools.staticdir.on': True,
|
||||
'tools.staticdir.dir': '.'}}
|
||||
'/': {
|
||||
'tools.staticdir.root': js_dir,
|
||||
'tools.staticdir.on': True,
|
||||
'tools.staticdir.dir': '.'
|
||||
}
|
||||
}
|
||||
cherrypy.tree.mount(None, js_url, config)
|
||||
logger.debug('Serving javascript directory %s on %s', js_dir, js_url)
|
||||
|
||||
@ -131,9 +131,12 @@ def setup_server():
|
||||
manual_url = '/'.join([cfg.server_dir, 'help/manual/images']) \
|
||||
.replace('//', '/')
|
||||
config = {
|
||||
'/': {'tools.staticdir.root': manual_dir,
|
||||
'tools.staticdir.on': True,
|
||||
'tools.staticdir.dir': '.'}}
|
||||
'/': {
|
||||
'tools.staticdir.root': manual_dir,
|
||||
'tools.staticdir.on': True,
|
||||
'tools.staticdir.dir': '.'
|
||||
}
|
||||
}
|
||||
cherrypy.tree.mount(None, manual_url, config)
|
||||
logger.debug('Serving manual images %s on %s', manual_dir, manual_url)
|
||||
|
||||
@ -144,9 +147,12 @@ def setup_server():
|
||||
continue
|
||||
|
||||
config = {
|
||||
'/': {'tools.staticdir.root': static_dir,
|
||||
'tools.staticdir.on': True,
|
||||
'tools.staticdir.dir': '.'}}
|
||||
'/': {
|
||||
'tools.staticdir.root': static_dir,
|
||||
'tools.staticdir.on': True,
|
||||
'tools.staticdir.dir': '.'
|
||||
}
|
||||
}
|
||||
urlprefix = "%s%s" % (django.conf.settings.STATIC_URL, module_name)
|
||||
cherrypy.tree.mount(None, urlprefix, config)
|
||||
logger.debug('Serving static directory %s on %s', static_dir,
|
||||
@ -168,25 +174,25 @@ def configure_django():
|
||||
'formatters': {
|
||||
'default': {
|
||||
'format':
|
||||
'[%(asctime)s] %(name)-14s %(levelname)-8s %(message)s',
|
||||
}
|
||||
},
|
||||
'[%(asctime)s] %(name)-14s %(levelname)-8s %(message)s',
|
||||
}
|
||||
},
|
||||
'handlers': {
|
||||
'file': {
|
||||
'class': 'logging.FileHandler',
|
||||
'filename': cfg.status_log_file,
|
||||
'formatter': 'default'
|
||||
},
|
||||
},
|
||||
'console': {
|
||||
'class': 'logging.StreamHandler',
|
||||
'formatter': 'default'
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
'root': {
|
||||
'handlers': ['console', 'file'],
|
||||
'level': 'DEBUG' if cfg.debug else 'INFO'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
templates = [
|
||||
{
|
||||
@ -225,35 +231,45 @@ def configure_django():
|
||||
if cfg.secure_proxy_ssl_header:
|
||||
secure_proxy_ssl_header = (cfg.secure_proxy_ssl_header, 'https')
|
||||
|
||||
pwd = 'django.contrib.auth.password_validation'
|
||||
|
||||
django.conf.settings.configure(
|
||||
ALLOWED_HOSTS=['*'],
|
||||
AUTH_PASSWORD_VALIDATORS=[
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
||||
'NAME': '{}.UserAttributeSimilarityValidator'.format(pwd),
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
||||
'NAME': '{}.MinimumLengthValidator'.format(pwd),
|
||||
'OPTIONS': {
|
||||
'min_length': 8,
|
||||
}
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
||||
'NAME': '{}.CommonPasswordValidator'.format(pwd),
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
||||
'NAME': '{}.NumericPasswordValidator'.format(pwd),
|
||||
},
|
||||
],
|
||||
AXES_LOCKOUT_URL='locked',
|
||||
AXES_LOCKOUT_URL='locked/',
|
||||
AXES_BEHIND_REVERSE_PROXY=True,
|
||||
CACHES={'default':
|
||||
{'BACKEND': 'django.core.cache.backends.dummy.DummyCache'}},
|
||||
CAPTCHA_FONT_PATH=['/usr/share/fonts/truetype/ttf-bitstream-vera/Vera.ttf'],
|
||||
CACHES={
|
||||
'default': {
|
||||
'BACKEND': 'django.core.cache.backends.dummy.DummyCache'
|
||||
}
|
||||
},
|
||||
CAPTCHA_FONT_PATH=[
|
||||
'/usr/share/fonts/truetype/ttf-bitstream-vera/Vera.ttf'
|
||||
],
|
||||
CAPTCHA_LENGTH=6,
|
||||
CAPTCHA_FLITE_PATH='/usr/bin/flite',
|
||||
DATABASES={'default':
|
||||
{'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': cfg.store_file}},
|
||||
DATABASES={
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': cfg.store_file
|
||||
}
|
||||
},
|
||||
DEBUG=cfg.debug,
|
||||
FORCE_SCRIPT_NAME=cfg.server_dir,
|
||||
INSTALLED_APPS=applications,
|
||||
@ -274,8 +290,7 @@ def configure_django():
|
||||
'plinth.middleware.AdminRequiredMiddleware',
|
||||
'plinth.middleware.FirstSetupMiddleware',
|
||||
'plinth.modules.first_boot.middleware.FirstBootMiddleware',
|
||||
'plinth.middleware.SetupMiddleware',
|
||||
),
|
||||
'plinth.middleware.SetupMiddleware', ),
|
||||
ROOT_URLCONF='plinth.urls',
|
||||
SECURE_BROWSER_XSS_FILTER=True,
|
||||
SECURE_CONTENT_TYPE_NOSNIFF=True,
|
||||
@ -284,8 +299,11 @@ def configure_django():
|
||||
SESSION_FILE_PATH=sessions_directory,
|
||||
STATIC_URL='/'.join([cfg.server_dir, 'static/']).replace('//', '/'),
|
||||
# STRONGHOLD_PUBLIC_URLS=(r'^captcha/', ),
|
||||
STRONGHOLD_PUBLIC_NAMED_URLS=('captcha-image', 'captcha-image-2x',
|
||||
'captcha-audio', 'captcha-refresh', ),
|
||||
STRONGHOLD_PUBLIC_NAMED_URLS=(
|
||||
'captcha-image',
|
||||
'captcha-image-2x',
|
||||
'captcha-audio',
|
||||
'captcha-refresh', ),
|
||||
TEMPLATES=templates,
|
||||
USE_L10N=True,
|
||||
USE_X_FORWARDED_HOST=cfg.use_x_forwarded_host)
|
||||
|
||||
29
plinth/axes_app_config.py
Normal file
29
plinth/axes_app_config.py
Normal file
@ -0,0 +1,29 @@
|
||||
#
|
||||
# This file is part of Plinth.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
"""
|
||||
Overridden AppConfig from django-axes to avoid monkey-patched LoginView
|
||||
"""
|
||||
|
||||
from django import apps
|
||||
|
||||
|
||||
class AppConfig(apps.AppConfig):
|
||||
name = 'axes'
|
||||
|
||||
def ready(self):
|
||||
# Signals must be loaded for axes to get the login_failed signals
|
||||
from axes import signals # isort:skip
|
||||
@ -18,22 +18,20 @@
|
||||
Views for the Single Sign On module of Plinth
|
||||
"""
|
||||
|
||||
import logging
|
||||
import os
|
||||
import urllib
|
||||
import logging
|
||||
|
||||
from .forms import AuthenticationForm
|
||||
|
||||
from plinth import actions
|
||||
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.contrib.auth import REDIRECT_FIELD_NAME
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.contrib.auth.views import LoginView, LogoutView
|
||||
from django.http import HttpResponseRedirect
|
||||
|
||||
from django.shortcuts import render_to_response
|
||||
|
||||
from axes.decorators import axes_form_invalid
|
||||
from axes.utils import reset
|
||||
from plinth import actions
|
||||
|
||||
from .forms import AuthenticationForm
|
||||
|
||||
PRIVATE_KEY_FILE_NAME = 'privkey.pem'
|
||||
SSO_COOKIE_NAME = 'auth_pubtkt'
|
||||
@ -70,6 +68,10 @@ class SSOLoginView(LoginView):
|
||||
else:
|
||||
return response
|
||||
|
||||
@axes_form_invalid
|
||||
def form_invalid(self, *args, **kwargs):
|
||||
return super(SSOLoginView, self).form_invalid(*args, **kwargs)
|
||||
|
||||
|
||||
class CaptchaLoginView(LoginView):
|
||||
redirect_authenticated_user = True
|
||||
|
||||
@ -21,35 +21,30 @@ URLs for the Users module
|
||||
from django.conf.urls import url
|
||||
from django.urls import reverse_lazy
|
||||
|
||||
from stronghold.decorators import public
|
||||
from plinth.utils import non_admin_view
|
||||
from axes.decorators import axes_dispatch
|
||||
from plinth.modules.sso.views import SSOLoginView, SSOLogoutView
|
||||
from . import views
|
||||
from plinth.utils import non_admin_view
|
||||
from stronghold.decorators import public
|
||||
|
||||
from axes.decorators import watch_login
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^sys/users/$', views.UserList.as_view(), name='index'),
|
||||
url(r'^sys/users/create/$', views.UserCreate.as_view(), name='create'),
|
||||
url(r'^sys/users/(?P<slug>[\w.@+-]+)/edit/$',
|
||||
non_admin_view(views.UserUpdate.as_view()),
|
||||
name='edit'),
|
||||
non_admin_view(views.UserUpdate.as_view()), name='edit'),
|
||||
url(r'^sys/users/(?P<slug>[\w.@+-]+)/delete/$',
|
||||
views.UserDelete.as_view(),
|
||||
name='delete'),
|
||||
views.UserDelete.as_view(), name='delete'),
|
||||
url(r'^sys/users/(?P<slug>[\w.@+-]+)/change_password/$',
|
||||
non_admin_view(views.UserChangePassword.as_view()),
|
||||
name='change_password'),
|
||||
|
||||
# Authnz is handled by SSO
|
||||
url(r'^accounts/login/$',
|
||||
public(watch_login(SSOLoginView.as_view())),
|
||||
name='login'),
|
||||
public(axes_dispatch(SSOLoginView.as_view())), name='login'),
|
||||
url(r'^accounts/logout/$',
|
||||
non_admin_view(SSOLogoutView.as_view()),
|
||||
{'next_page': reverse_lazy('index')},
|
||||
name='logout'),
|
||||
{'next_page': reverse_lazy('index')}, name='logout'),
|
||||
url(r'^users/firstboot/$',
|
||||
public(views.FirstBootView.as_view()),
|
||||
name='firstboot'),
|
||||
public(views.FirstBootView.as_view()), name='firstboot'),
|
||||
]
|
||||
|
||||
@ -17,10 +17,10 @@
|
||||
"""
|
||||
Django URLconf file containing all urls
|
||||
"""
|
||||
from captcha import views as cviews
|
||||
from django.conf.urls import url
|
||||
from django.views.generic import TemplateView
|
||||
|
||||
from captcha import views as cviews
|
||||
from plinth.modules.sso.views import CaptchaLoginView
|
||||
from stronghold.decorators import public
|
||||
|
||||
@ -29,20 +29,18 @@ from . import views
|
||||
urlpatterns = [
|
||||
url(r'^$', views.index, name='index'),
|
||||
url(r'^apps/$',
|
||||
TemplateView.as_view(template_name='apps.html'),
|
||||
name='apps'),
|
||||
TemplateView.as_view(template_name='apps.html'), name='apps'),
|
||||
url(r'^sys/$', views.system_index, name='system'),
|
||||
|
||||
# captcha urls are public
|
||||
url(r'image/(?P<key>\w+)/$',
|
||||
public(cviews.captcha_image),
|
||||
name='captcha-image',
|
||||
public(cviews.captcha_image), name='captcha-image',
|
||||
kwargs={'scale': 1}),
|
||||
url(r'image/(?P<key>\w+)@2/$',
|
||||
public(cviews.captcha_image),
|
||||
name='captcha-image-2x',
|
||||
public(cviews.captcha_image), name='captcha-image-2x',
|
||||
kwargs={'scale': 2}),
|
||||
url(r'audio/(?P<key>\w+)/$', public(cviews.captcha_audio), name='captcha-audio'),
|
||||
url(r'audio/(?P<key>\w+)/$',
|
||||
public(cviews.captcha_audio), name='captcha-audio'),
|
||||
url(r'refresh/$', public(cviews.captcha_refresh), name='captcha-refresh'),
|
||||
url(r'locked/$', public(CaptchaLoginView.as_view()), name='locked_out'),
|
||||
]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user