Activate secure settings by default

pull/84/head
JensDiemer 2021-12-05 16:22:24 +01:00
rodzic e7cb83633c
commit d019a98b78
8 zmienionych plików z 194 dodań i 17 usunięć

Wyświetl plik

@ -10,6 +10,7 @@ from inventory.models import ItemImageModel
from inventory.tests.fixtures.users import get_normal_pyinventory_user
# @override_settings(SECURE_SSL_REDIRECT=False)
class ItemImagesTestCase(TestCase):
def test_basics(self):
with mock.patch('secrets.token_urlsafe', return_value='user1token'):
@ -40,23 +41,47 @@ class ItemImagesTestCase(TestCase):
url = image_instance.image.url
assert url == '/media/user1token/12345678901234567890/mock_img.jpeg'
# HTTP -> HTTPS redirect:
response = self.client.get(
'/media/user1token/12345678901234567890/mock_img.jpeg',
secure=False
)
self.assertRedirects(
response,
status_code=301,
expected_url='https://testserver/media/user1token/12345678901234567890/mock_img.jpeg',
fetch_redirect_response=False,
)
# Anonymous has no access:
response = self.client.get('/media/user1token/12345678901234567890/mock_img.jpeg')
response = self.client.get(
'/media/user1token/12345678901234567890/mock_img.jpeg',
secure=True,
)
assert response.status_code == 403
# Can't access with wrong user:
self.client.force_login(pyinventory_user2)
response = self.client.get('/media/user1token/12345678901234567890/mock_img.jpeg')
response = self.client.get(
'/media/user1token/12345678901234567890/mock_img.jpeg',
secure=True,
)
assert response.status_code == 403
# Can access with the right user:
self.client.force_login(pyinventory_user1)
response = self.client.get('/media/user1token/12345678901234567890/mock_img.jpeg')
response = self.client.get(
'/media/user1token/12345678901234567890/mock_img.jpeg',
secure=True,
)
assert response.status_code == 200
assert isinstance(response, FileResponse)
assert response.getvalue() == image_instance.image.open('rb').read()
# Test whats happen, if token was deleted
UserMediaTokenModel.objects.all().delete()
response = self.client.get('/media/user1token/12345678901234567890/mock_img.jpeg')
response = self.client.get(
'/media/user1token/12345678901234567890/mock_img.jpeg',
secure=True,
)
assert response.status_code == 400 # SuspiciousOperation -> HttpResponseBadRequest

Wyświetl plik

@ -131,6 +131,31 @@ TEMPLATES = [
},
]
# _____________________________________________________________________________
# Mark CSRF cookie as "secure" -> browsers sent cookie only with an HTTPS connection:
CSRF_COOKIE_SECURE = True
# Mark session cookie as "secure" -> browsers sent cookie only with an HTTPS connection:
SESSION_COOKIE_SECURE = True
# HTTP header/value combination that signifies a request is secure
# Your nginx.conf must set "X-Forwarded-Protocol" proxy header!
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTOCOL', 'https')
# SecurityMiddleware should redirects all non-HTTPS requests to HTTPS:
SECURE_SSL_REDIRECT = True
# SecurityMiddleware should preload directive to the HTTP Strict Transport Security header:
SECURE_HSTS_PRELOAD = True
# Instruct modern browsers to refuse to connect to your domain name via an insecure connection:
SECURE_HSTS_SECONDS = 3600
# SecurityMiddleware should add the "includeSubDomains" directive to the Strict-Transport-Security
# header: All subdomains of your domain should be served exclusively via SSL!
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
# _____________________________________________________________________________
# Internationalization

Wyświetl plik

@ -34,6 +34,16 @@ DATABASES = {
}
}
print(f'Use Database: {DATABASES["default"]["NAME"]!r}', file=__sys.stderr)
# _____________________________________________________________________________
# Disable security features, because development server doesn't support HTTPS
CSRF_COOKIE_SECURE = False
SESSION_COOKIE_SECURE = False
SECURE_PROXY_SSL_HEADER = None
SECURE_SSL_REDIRECT = False
SECURE_HSTS_PRELOAD = False
SECURE_HSTS_SECONDS = 0
SECURE_HSTS_INCLUDE_SUBDOMAINS = False
# _____________________________________________________________________________
# AlwaysLoggedInAsSuperUser

Wyświetl plik

@ -10,7 +10,7 @@ DATABASES = {
}
}
SECRET_KEY = 'No individual secret for tests ;)'
SECRET_KEY = 'No individual secret... But this settings should only be used in tests ;)'
# Run the tests as on production: Without DBEUG:
DEBUG = False

Wyświetl plik

@ -2,7 +2,7 @@ import os
import unittest
from django.contrib.auth.models import User
from django.test import TestCase
from django.test import TestCase, override_settings
from django_processinfo.models import ProcessInfo, SiteStatistics
from django_tools.selenium.chromedriver import chromium_available
from django_tools.selenium.django import (
@ -22,14 +22,15 @@ class AdminAnonymousTests(TestCase):
"""
def test_login_en(self):
response = self.client.get("/admin/", HTTP_ACCEPT_LANGUAGE="en")
self.assertRedirects(response, expected_url="/admin/login/?next=/admin/")
response = self.client.get('/admin/', secure=True, HTTP_ACCEPT_LANGUAGE='en')
self.assertRedirects(response, expected_url='/admin/login/?next=/admin/', fetch_redirect_response=False)
def test_login_de(self):
response = self.client.get("/admin/", HTTP_ACCEPT_LANGUAGE="de")
self.assertRedirects(response, expected_url="/admin/login/?next=/admin/")
response = self.client.get('/admin/', secure=True, HTTP_ACCEPT_LANGUAGE='de')
self.assertRedirects(response, expected_url='/admin/login/?next=/admin/', fetch_redirect_response=False)
@override_settings(SECURE_SSL_REDIRECT=False)
class ProcessinfoAdminTestCase(TestCase):
@classmethod
def setUpTestData(cls):
@ -87,6 +88,7 @@ class ProcessinfoAdminTestCase(TestCase):
@unittest.skipIf('CI' in os.environ, 'Skip, selenium tests does not work on CI run!')
@unittest.skipUnless(chromium_available(), "Skip because Chromium is not available!")
@override_settings(SECURE_SSL_REDIRECT=False)
class AdminChromiumTests(SeleniumChromiumStaticLiveServerTestCase):
def test_admin_login_page(self):
self.driver.get(self.live_server_url + "/admin/login/")
@ -97,6 +99,7 @@ class AdminChromiumTests(SeleniumChromiumStaticLiveServerTestCase):
@unittest.skipIf('CI' in os.environ, 'Skip, selenium tests does not work on CI run!')
@unittest.skipUnless(firefox_available(), "Skip because Firefox is not available!")
@override_settings(SECURE_SSL_REDIRECT=False)
class AdminFirefoxTests(SeleniumFirefoxStaticLiveServerTestCase):
def test_admin_login_page(self):
self.driver.get(self.live_server_url + "/admin/login/")

Wyświetl plik

@ -7,7 +7,7 @@ from bx_django_utils.test_utils.html_assertion import HtmlAssertionMixin
from bx_py_utils.test_utils.snapshot import assert_html_snapshot
from django.contrib.auth.models import User
from django.template.defaulttags import CsrfTokenNode, NowNode
from django.test import TestCase
from django.test import TestCase, override_settings
from django.utils import timezone
from django_tools.unittest_utils.mockup import ImageDummy
from model_bakery import baker
@ -46,15 +46,42 @@ ITEM_FORM_DEFAULTS = {
ITEM_FORM_DEFAULTS = tuple(ITEM_FORM_DEFAULTS.items())
class AdminAnonymousTests(TestCase):
class AdminAnonymousTests(HtmlAssertionMixin, TestCase):
def test_login(self):
response = self.client.get('/admin/inventory/itemmodel/add/', HTTP_ACCEPT_LANGUAGE='en')
# HTTP -> HTTPS redirect:
response = self.client.get('/admin/', HTTP_ACCEPT_LANGUAGE='en')
self.assertRedirects(
response,
expected_url='/admin/login/?next=/admin/inventory/itemmodel/add/'
expected_url='https://testserver/admin/',
status_code=301, # Permanent redirect
fetch_redirect_response=False
)
response = self.client.get(
path='/admin/inventory/itemmodel/add/',
secure=True,
HTTP_ACCEPT_LANGUAGE='en'
)
self.assertRedirects(
response,
expected_url='/admin/login/?next=/admin/inventory/itemmodel/add/',
fetch_redirect_response=False
)
with mock.patch.object(CsrfTokenNode, 'render', return_value='MockedCsrfTokenNode'):
response = self.client.get(
path='/admin/login/',
secure=True,
HTTP_ACCEPT_LANGUAGE='en'
)
self.assert_html_parts(response, parts=(
f'<title>Log in | PyInventory v{__version__}</title>',
'<label class="required" for="id_username">Username:</label>',
'<label class="required" for="id_password">Password:</label>',
))
assert_html_response_snapshot(response, validate=False)
@override_settings(SECURE_SSL_REDIRECT=False)
class AdminTestCase(HtmlAssertionMixin, TestCase):
@classmethod
def setUpTestData(cls):

Wyświetl plik

@ -0,0 +1,81 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>
Log in | PyInventory v0.12.0
</title>
<link href="/static/admin/css/base.css" rel="stylesheet" type="text/css"/>
<link href="/static/admin/css/nav_sidebar.css" rel="stylesheet" type="text/css"/>
<script defer="" src="/static/admin/js/nav_sidebar.js">
</script>
<link href="/static/admin/css/login.css" rel="stylesheet" type="text/css"/>
<style>
.form-row {display: none;}
</style>
<meta content="notranslate" name="google"/>
<meta content="noindex,nofollow" name="robots">
<link href="/static/inventory.css" rel="stylesheet" type="text/css"/>
<meta content="user-scalable=no, width=device-width, initial-scale=1.0, maximum-scale=1.0" name="viewport"/>
<link href="/static/admin/css/responsive.css" rel="stylesheet" type="text/css"/>
<meta content="NONE,NOARCHIVE" name="robots"/>
</meta>
</head>
<body class="login" data-admin-utc-offset="3600">
<!-- Container -->
<div id="container">
<!-- Header -->
<div id="header">
<div id="branding">
</div>
</div>
<!-- END Header -->
<div class="main shifted" id="main">
<div class="content">
<!-- Content -->
<div class="colM" id="content">
<script>
document.write('<fo'+'rm act'+'ion="/admin/login/" met'+'hod="po'+'st" id="lo'+'gin-fo'+'rm">');
</script>
MockedCsrfTokenNode
<div class="form-row">
<p class="required">
<label class="required" for="id_username">
Username:
</label>
<input autocapitalize="none" autocomplete="username" autofocus="" id="id_username" maxlength="150" name="username" required="" type="text"/>
</p>
<p class="required">
<label class="required" for="id_password">
Password:
</label>
<input autocomplete="current-password" id="id_password" name="password" required="" type="password"/>
</p>
</div>
<div class="submit-row">
<noscript>
Please enable JavaScript ;)
</noscript>
<label>
</label>
<script>
document.write('<in'+'put type="sub'+'mit" val'+'ue="Log in">');
</script>
</div>
<script>
'use strict';
document.write('</fo'+'rm>');
document.addEventListener('DOMContentLoaded', () => {
for (const object of document.querySelectorAll('.form-row')) {
object.style.display = "block";
}
});
</script>
<br class="clear"/>
</div>
<!-- END Content -->
</div>
</div>
</div>
<!-- END Container -->
</body>
</html>

Wyświetl plik

@ -3,7 +3,7 @@ from unittest import mock
from bx_django_utils.test_utils.html_assertion import HtmlAssertionMixin
from django.contrib.auth.models import User
from django.template.defaulttags import CsrfTokenNode, NowNode
from django.test import TestCase
from django.test import TestCase, override_settings
from django_tools.unittest_utils.mockup import ImageDummy
from model_bakery import baker
@ -15,13 +15,19 @@ from inventory_project.tests.temp_utils import assert_html_response_snapshot
class AdminAnonymousTests(TestCase):
def test_login(self):
response = self.client.get('/admin/inventory/memomodel/add/', HTTP_ACCEPT_LANGUAGE='en')
response = self.client.get(
'/admin/inventory/memomodel/add/',
secure=True,
HTTP_ACCEPT_LANGUAGE='en'
)
self.assertRedirects(
response,
expected_url='/admin/login/?next=/admin/inventory/memomodel/add/'
expected_url='/admin/login/?next=/admin/inventory/memomodel/add/',
fetch_redirect_response=False,
)
@override_settings(SECURE_SSL_REDIRECT=False)
class AdminTestCase(HtmlAssertionMixin, TestCase):
@classmethod
def setUpTestData(cls):