plain 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- plain/README.md +33 -0
- plain/__main__.py +5 -0
- plain/assets/README.md +56 -0
- plain/assets/__init__.py +6 -0
- plain/assets/finders.py +233 -0
- plain/assets/preflight.py +14 -0
- plain/assets/storage.py +916 -0
- plain/assets/utils.py +52 -0
- plain/assets/whitenoise/__init__.py +5 -0
- plain/assets/whitenoise/base.py +259 -0
- plain/assets/whitenoise/compress.py +189 -0
- plain/assets/whitenoise/media_types.py +137 -0
- plain/assets/whitenoise/middleware.py +197 -0
- plain/assets/whitenoise/responders.py +286 -0
- plain/assets/whitenoise/storage.py +178 -0
- plain/assets/whitenoise/string_utils.py +13 -0
- plain/cli/README.md +123 -0
- plain/cli/__init__.py +3 -0
- plain/cli/cli.py +439 -0
- plain/cli/formatting.py +61 -0
- plain/cli/packages.py +73 -0
- plain/cli/print.py +9 -0
- plain/cli/startup.py +33 -0
- plain/csrf/README.md +3 -0
- plain/csrf/middleware.py +466 -0
- plain/csrf/views.py +10 -0
- plain/debug.py +23 -0
- plain/exceptions.py +242 -0
- plain/forms/README.md +14 -0
- plain/forms/__init__.py +8 -0
- plain/forms/boundfield.py +58 -0
- plain/forms/exceptions.py +11 -0
- plain/forms/fields.py +1030 -0
- plain/forms/forms.py +297 -0
- plain/http/README.md +1 -0
- plain/http/__init__.py +51 -0
- plain/http/cookie.py +20 -0
- plain/http/multipartparser.py +743 -0
- plain/http/request.py +754 -0
- plain/http/response.py +719 -0
- plain/internal/__init__.py +0 -0
- plain/internal/files/README.md +3 -0
- plain/internal/files/__init__.py +3 -0
- plain/internal/files/base.py +161 -0
- plain/internal/files/locks.py +127 -0
- plain/internal/files/move.py +102 -0
- plain/internal/files/temp.py +79 -0
- plain/internal/files/uploadedfile.py +150 -0
- plain/internal/files/uploadhandler.py +254 -0
- plain/internal/files/utils.py +78 -0
- plain/internal/handlers/__init__.py +0 -0
- plain/internal/handlers/base.py +133 -0
- plain/internal/handlers/exception.py +145 -0
- plain/internal/handlers/wsgi.py +216 -0
- plain/internal/legacy/__init__.py +0 -0
- plain/internal/legacy/__main__.py +12 -0
- plain/internal/legacy/management/__init__.py +414 -0
- plain/internal/legacy/management/base.py +692 -0
- plain/internal/legacy/management/color.py +113 -0
- plain/internal/legacy/management/commands/__init__.py +0 -0
- plain/internal/legacy/management/commands/collectstatic.py +297 -0
- plain/internal/legacy/management/sql.py +67 -0
- plain/internal/legacy/management/utils.py +175 -0
- plain/json.py +40 -0
- plain/logs/README.md +24 -0
- plain/logs/__init__.py +5 -0
- plain/logs/configure.py +39 -0
- plain/logs/loggers.py +74 -0
- plain/logs/utils.py +46 -0
- plain/middleware/README.md +3 -0
- plain/middleware/__init__.py +0 -0
- plain/middleware/clickjacking.py +52 -0
- plain/middleware/common.py +87 -0
- plain/middleware/gzip.py +64 -0
- plain/middleware/security.py +64 -0
- plain/packages/README.md +41 -0
- plain/packages/__init__.py +4 -0
- plain/packages/config.py +259 -0
- plain/packages/registry.py +438 -0
- plain/paginator.py +187 -0
- plain/preflight/README.md +3 -0
- plain/preflight/__init__.py +38 -0
- plain/preflight/compatibility/__init__.py +0 -0
- plain/preflight/compatibility/django_4_0.py +20 -0
- plain/preflight/files.py +19 -0
- plain/preflight/messages.py +88 -0
- plain/preflight/registry.py +72 -0
- plain/preflight/security/__init__.py +0 -0
- plain/preflight/security/base.py +268 -0
- plain/preflight/security/csrf.py +40 -0
- plain/preflight/urls.py +117 -0
- plain/runtime/README.md +75 -0
- plain/runtime/__init__.py +61 -0
- plain/runtime/global_settings.py +199 -0
- plain/runtime/user_settings.py +353 -0
- plain/signals/README.md +14 -0
- plain/signals/__init__.py +5 -0
- plain/signals/dispatch/__init__.py +9 -0
- plain/signals/dispatch/dispatcher.py +320 -0
- plain/signals/dispatch/license.txt +35 -0
- plain/signing.py +299 -0
- plain/templates/README.md +20 -0
- plain/templates/__init__.py +6 -0
- plain/templates/core.py +24 -0
- plain/templates/jinja/README.md +227 -0
- plain/templates/jinja/__init__.py +22 -0
- plain/templates/jinja/defaults.py +119 -0
- plain/templates/jinja/extensions.py +39 -0
- plain/templates/jinja/filters.py +28 -0
- plain/templates/jinja/globals.py +19 -0
- plain/test/README.md +3 -0
- plain/test/__init__.py +16 -0
- plain/test/client.py +985 -0
- plain/test/utils.py +255 -0
- plain/urls/README.md +3 -0
- plain/urls/__init__.py +40 -0
- plain/urls/base.py +118 -0
- plain/urls/conf.py +94 -0
- plain/urls/converters.py +66 -0
- plain/urls/exceptions.py +9 -0
- plain/urls/resolvers.py +731 -0
- plain/utils/README.md +3 -0
- plain/utils/__init__.py +0 -0
- plain/utils/_os.py +52 -0
- plain/utils/cache.py +327 -0
- plain/utils/connection.py +84 -0
- plain/utils/crypto.py +76 -0
- plain/utils/datastructures.py +345 -0
- plain/utils/dateformat.py +329 -0
- plain/utils/dateparse.py +154 -0
- plain/utils/dates.py +76 -0
- plain/utils/deconstruct.py +54 -0
- plain/utils/decorators.py +90 -0
- plain/utils/deprecation.py +6 -0
- plain/utils/duration.py +44 -0
- plain/utils/email.py +12 -0
- plain/utils/encoding.py +235 -0
- plain/utils/functional.py +456 -0
- plain/utils/hashable.py +26 -0
- plain/utils/html.py +401 -0
- plain/utils/http.py +374 -0
- plain/utils/inspect.py +73 -0
- plain/utils/ipv6.py +46 -0
- plain/utils/itercompat.py +8 -0
- plain/utils/module_loading.py +69 -0
- plain/utils/regex_helper.py +353 -0
- plain/utils/safestring.py +72 -0
- plain/utils/termcolors.py +221 -0
- plain/utils/text.py +518 -0
- plain/utils/timesince.py +138 -0
- plain/utils/timezone.py +244 -0
- plain/utils/tree.py +126 -0
- plain/validators.py +603 -0
- plain/views/README.md +268 -0
- plain/views/__init__.py +18 -0
- plain/views/base.py +107 -0
- plain/views/csrf.py +24 -0
- plain/views/errors.py +25 -0
- plain/views/exceptions.py +4 -0
- plain/views/forms.py +76 -0
- plain/views/objects.py +229 -0
- plain/views/redirect.py +72 -0
- plain/views/templates.py +66 -0
- plain/wsgi.py +11 -0
- plain-0.1.0.dist-info/LICENSE +85 -0
- plain-0.1.0.dist-info/METADATA +51 -0
- plain-0.1.0.dist-info/RECORD +169 -0
- plain-0.1.0.dist-info/WHEEL +4 -0
- plain-0.1.0.dist-info/entry_points.txt +3 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from plain.runtime import settings
|
|
2
|
+
|
|
3
|
+
from .. import Warning, register
|
|
4
|
+
|
|
5
|
+
W003 = Warning(
|
|
6
|
+
"You don't appear to be using Plain's built-in "
|
|
7
|
+
"cross-site request forgery protection via the middleware "
|
|
8
|
+
"('plain.csrf.middleware.CsrfViewMiddleware' is not in your "
|
|
9
|
+
"MIDDLEWARE). Enabling the middleware is the safest approach "
|
|
10
|
+
"to ensure you don't leave any holes.",
|
|
11
|
+
id="security.W003",
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
W016 = Warning(
|
|
15
|
+
"You have 'plain.csrf.middleware.CsrfViewMiddleware' in your "
|
|
16
|
+
"MIDDLEWARE, but you have not set CSRF_COOKIE_SECURE to True. "
|
|
17
|
+
"Using a secure-only CSRF cookie makes it more difficult for network "
|
|
18
|
+
"traffic sniffers to steal the CSRF token.",
|
|
19
|
+
id="security.W016",
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _csrf_middleware():
|
|
24
|
+
return "plain.csrf.middleware.CsrfViewMiddleware" in settings.MIDDLEWARE
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@register(deploy=True)
|
|
28
|
+
def check_csrf_middleware(package_configs, **kwargs):
|
|
29
|
+
passed_check = _csrf_middleware()
|
|
30
|
+
return [] if passed_check else [W003]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@register(deploy=True)
|
|
34
|
+
def check_csrf_cookie_secure(package_configs, **kwargs):
|
|
35
|
+
passed_check = (
|
|
36
|
+
settings.CSRF_USE_SESSIONS
|
|
37
|
+
or not _csrf_middleware()
|
|
38
|
+
or settings.CSRF_COOKIE_SECURE is True
|
|
39
|
+
)
|
|
40
|
+
return [] if passed_check else [W016]
|
plain/preflight/urls.py
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
from collections import Counter
|
|
2
|
+
|
|
3
|
+
from plain.runtime import settings
|
|
4
|
+
|
|
5
|
+
from . import Error, Warning, register
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@register
|
|
9
|
+
def check_url_config(package_configs, **kwargs):
|
|
10
|
+
if getattr(settings, "ROOT_URLCONF", None):
|
|
11
|
+
from plain.urls import get_resolver
|
|
12
|
+
|
|
13
|
+
resolver = get_resolver()
|
|
14
|
+
return check_resolver(resolver)
|
|
15
|
+
return []
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def check_resolver(resolver):
|
|
19
|
+
"""
|
|
20
|
+
Recursively check the resolver.
|
|
21
|
+
"""
|
|
22
|
+
check_method = getattr(resolver, "check", None)
|
|
23
|
+
if check_method is not None:
|
|
24
|
+
return check_method()
|
|
25
|
+
elif not hasattr(resolver, "resolve"):
|
|
26
|
+
return get_warning_for_invalid_pattern(resolver)
|
|
27
|
+
else:
|
|
28
|
+
return []
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@register
|
|
32
|
+
def check_url_namespaces_unique(package_configs, **kwargs):
|
|
33
|
+
"""
|
|
34
|
+
Warn if URL namespaces used in applications aren't unique.
|
|
35
|
+
"""
|
|
36
|
+
if not getattr(settings, "ROOT_URLCONF", None):
|
|
37
|
+
return []
|
|
38
|
+
|
|
39
|
+
from plain.urls import get_resolver
|
|
40
|
+
|
|
41
|
+
resolver = get_resolver()
|
|
42
|
+
all_namespaces = _load_all_namespaces(resolver)
|
|
43
|
+
counter = Counter(all_namespaces)
|
|
44
|
+
non_unique_namespaces = [n for n, count in counter.items() if count > 1]
|
|
45
|
+
errors = []
|
|
46
|
+
for namespace in non_unique_namespaces:
|
|
47
|
+
errors.append(
|
|
48
|
+
Warning(
|
|
49
|
+
"URL namespace '{}' isn't unique. You may not be able to reverse "
|
|
50
|
+
"all URLs in this namespace".format(namespace),
|
|
51
|
+
id="urls.W005",
|
|
52
|
+
)
|
|
53
|
+
)
|
|
54
|
+
return errors
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _load_all_namespaces(resolver, parents=()):
|
|
58
|
+
"""
|
|
59
|
+
Recursively load all namespaces from URL patterns.
|
|
60
|
+
"""
|
|
61
|
+
url_patterns = getattr(resolver, "url_patterns", [])
|
|
62
|
+
namespaces = [
|
|
63
|
+
":".join(parents + (url.namespace,))
|
|
64
|
+
for url in url_patterns
|
|
65
|
+
if getattr(url, "namespace", None) is not None
|
|
66
|
+
]
|
|
67
|
+
for pattern in url_patterns:
|
|
68
|
+
namespace = getattr(pattern, "namespace", None)
|
|
69
|
+
current = parents
|
|
70
|
+
if namespace is not None:
|
|
71
|
+
current += (namespace,)
|
|
72
|
+
namespaces.extend(_load_all_namespaces(pattern, current))
|
|
73
|
+
return namespaces
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def get_warning_for_invalid_pattern(pattern):
|
|
77
|
+
"""
|
|
78
|
+
Return a list containing a warning that the pattern is invalid.
|
|
79
|
+
|
|
80
|
+
describe_pattern() cannot be used here, because we cannot rely on the
|
|
81
|
+
urlpattern having regex or name attributes.
|
|
82
|
+
"""
|
|
83
|
+
if isinstance(pattern, str):
|
|
84
|
+
hint = (
|
|
85
|
+
f"Try removing the string '{pattern}'. The list of urlpatterns should not "
|
|
86
|
+
"have a prefix string as the first element."
|
|
87
|
+
)
|
|
88
|
+
elif isinstance(pattern, tuple):
|
|
89
|
+
hint = "Try using path() instead of a tuple."
|
|
90
|
+
else:
|
|
91
|
+
hint = None
|
|
92
|
+
|
|
93
|
+
return [
|
|
94
|
+
Error(
|
|
95
|
+
"Your URL pattern {!r} is invalid. Ensure that urlpatterns is a list "
|
|
96
|
+
"of path() and/or re_path() instances.".format(pattern),
|
|
97
|
+
hint=hint,
|
|
98
|
+
id="urls.E004",
|
|
99
|
+
)
|
|
100
|
+
]
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
@register
|
|
104
|
+
def check_url_settings(package_configs, **kwargs):
|
|
105
|
+
errors = []
|
|
106
|
+
for name in ("ASSETS_URL",):
|
|
107
|
+
value = getattr(settings, name)
|
|
108
|
+
if value and not value.endswith("/"):
|
|
109
|
+
errors.append(E006(name))
|
|
110
|
+
return errors
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def E006(name):
|
|
114
|
+
return Error(
|
|
115
|
+
f"The {name} setting must end with a slash.",
|
|
116
|
+
id="urls.E006",
|
|
117
|
+
)
|
plain/runtime/README.md
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Runtime
|
|
2
|
+
|
|
3
|
+
Leverage user-settings at runtime.
|
|
4
|
+
|
|
5
|
+
## Settings
|
|
6
|
+
|
|
7
|
+
### Single-file
|
|
8
|
+
|
|
9
|
+
All of your settings go in `app/settings.py`.
|
|
10
|
+
That's how you do it!
|
|
11
|
+
|
|
12
|
+
The file itself is not much different than how Django does it,
|
|
13
|
+
but the location,
|
|
14
|
+
and a strong recommendation to only use the one file makes a big difference.
|
|
15
|
+
|
|
16
|
+
### Environment variables
|
|
17
|
+
|
|
18
|
+
It seems pretty well-accepted these days that storing settings in env vars is a good idea ([12factor.net](https://12factor.net/config)).
|
|
19
|
+
|
|
20
|
+
Your settings file should be looking at the environment for secrets or other values that might change between environments. For example:
|
|
21
|
+
|
|
22
|
+
```python
|
|
23
|
+
# app/settings.py
|
|
24
|
+
STRIPE_SECRET_KEY = environ["STRIPE_SECRET_KEY"]
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
#### Local development
|
|
28
|
+
|
|
29
|
+
In local development,
|
|
30
|
+
you should use `.env` files to set these values.
|
|
31
|
+
The `.env` should then be in your `.gitignore`!
|
|
32
|
+
|
|
33
|
+
It would seem like `.env.dev` would be a good idea,
|
|
34
|
+
but there's a chicken-and-egg problem with that.
|
|
35
|
+
You would then have to prefix most (or all) of your local commands with `PLAIN_ENV=dev` or otherwise configure your environment to do that for you.
|
|
36
|
+
Generally speaking,
|
|
37
|
+
a production `.env` shouldn't be committed in your repo anyway,
|
|
38
|
+
so using `.env` for local development is ok.
|
|
39
|
+
The downside to this is that it's harder to share your local settings with others,
|
|
40
|
+
but these also often contain real secrets which shouldn't be committed to your repo either!
|
|
41
|
+
More advanced `.env` sharing patterns are currently beyond the scope of Plain...
|
|
42
|
+
|
|
43
|
+
#### Production
|
|
44
|
+
|
|
45
|
+
TODO
|
|
46
|
+
|
|
47
|
+
## Minimum required settings
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
# SECURITY WARNING: keep the secret key used in production secret!
|
|
51
|
+
SECRET_KEY = environ["SECRET_KEY"]
|
|
52
|
+
|
|
53
|
+
# SECURITY WARNING: don't run with debug turned on in production!
|
|
54
|
+
DEBUG = environ.get("DEBUG", "false").lower() in ("true", "1", "yes")
|
|
55
|
+
|
|
56
|
+
MIDDLEWARE = [
|
|
57
|
+
"plain.middleware.security.SecurityMiddleware",
|
|
58
|
+
"plain.assets.whitenoise.middleware.WhiteNoiseMiddleware",
|
|
59
|
+
"plain.sessions.middleware.SessionMiddleware",
|
|
60
|
+
"plain.middleware.common.CommonMiddleware",
|
|
61
|
+
"plain.csrf.middleware.CsrfViewMiddleware",
|
|
62
|
+
"plain.auth.middleware.AuthenticationMiddleware",
|
|
63
|
+
"plain.middleware.clickjacking.XFrameOptionsMiddleware",
|
|
64
|
+
]
|
|
65
|
+
|
|
66
|
+
if DEBUG:
|
|
67
|
+
INSTALLED_PACKAGES += [
|
|
68
|
+
"plain.dev",
|
|
69
|
+
]
|
|
70
|
+
MIDDLEWARE += [
|
|
71
|
+
"plain.dev.RequestsMiddleware",
|
|
72
|
+
]
|
|
73
|
+
|
|
74
|
+
TIME_ZONE = "America/Chicago"
|
|
75
|
+
```
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import importlib.metadata
|
|
2
|
+
import sys
|
|
3
|
+
from os import environ
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from dotenv import load_dotenv
|
|
7
|
+
|
|
8
|
+
from .user_settings import LazySettings
|
|
9
|
+
|
|
10
|
+
try:
|
|
11
|
+
__version__ = importlib.metadata.version("plainframework")
|
|
12
|
+
except importlib.metadata.PackageNotFoundError:
|
|
13
|
+
__version__ = "dev"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
# Made available without setup or settings
|
|
17
|
+
APP_PATH = Path.cwd() / "app"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# from plain.runtime import settings
|
|
21
|
+
settings = LazySettings()
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class AppPathNotFound(RuntimeError):
|
|
25
|
+
pass
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def setup():
|
|
29
|
+
"""
|
|
30
|
+
Configure the settings (this happens as a side effect of accessing the
|
|
31
|
+
first setting), configure logging and populate the app registry.
|
|
32
|
+
"""
|
|
33
|
+
from plain.logs import configure_logging
|
|
34
|
+
from plain.packages import packages
|
|
35
|
+
|
|
36
|
+
if not APP_PATH.exists():
|
|
37
|
+
raise AppPathNotFound(
|
|
38
|
+
"No app directory found. Are you sure you're in a Plain project?"
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
# Automatically put the app dir on the Python path for convenience
|
|
42
|
+
if APP_PATH not in sys.path:
|
|
43
|
+
sys.path.insert(0, APP_PATH.as_posix())
|
|
44
|
+
|
|
45
|
+
# Load .env files automatically before settings
|
|
46
|
+
if app_env := environ.get("PLAIN_ENV", ""):
|
|
47
|
+
load_dotenv(f".env.{app_env}")
|
|
48
|
+
else:
|
|
49
|
+
load_dotenv(".env")
|
|
50
|
+
|
|
51
|
+
configure_logging(settings.LOGGING)
|
|
52
|
+
|
|
53
|
+
packages.populate(settings.INSTALLED_PACKAGES)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
__all__ = [
|
|
57
|
+
"setup",
|
|
58
|
+
"settings",
|
|
59
|
+
"APP_PATH",
|
|
60
|
+
"__version__",
|
|
61
|
+
]
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Default Plain settings. Override these with settings in the module pointed to
|
|
3
|
+
by the PLAIN_SETTINGS_MODULE environment variable.
|
|
4
|
+
"""
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from plain.runtime import APP_PATH as default_app_path
|
|
8
|
+
|
|
9
|
+
####################
|
|
10
|
+
# CORE #
|
|
11
|
+
####################
|
|
12
|
+
|
|
13
|
+
DEBUG: bool = False
|
|
14
|
+
|
|
15
|
+
PLAIN_TEMP_PATH: Path = default_app_path.parent / ".plain"
|
|
16
|
+
|
|
17
|
+
# Hosts/domain names that are valid for this site.
|
|
18
|
+
# "*" matches anything, ".example.com" matches example.com and all subdomains
|
|
19
|
+
ALLOWED_HOSTS: list[str] = []
|
|
20
|
+
|
|
21
|
+
# Local time zone for this installation. All choices can be found here:
|
|
22
|
+
# https://en.wikipedia.org/wiki/List_of_tz_zones_by_name (although not all
|
|
23
|
+
# systems may support all possibilities). When USE_TZ is True, this is
|
|
24
|
+
# interpreted as the default user time zone.
|
|
25
|
+
TIME_ZONE = "America/Chicago"
|
|
26
|
+
|
|
27
|
+
# If you set this to True, Plain will use timezone-aware datetimes.
|
|
28
|
+
USE_TZ = True
|
|
29
|
+
|
|
30
|
+
# Default charset to use for all Response objects, if a MIME type isn't
|
|
31
|
+
# manually specified. It's used to construct the Content-Type header.
|
|
32
|
+
DEFAULT_CHARSET = "utf-8"
|
|
33
|
+
|
|
34
|
+
# List of strings representing installed packages.
|
|
35
|
+
INSTALLED_PACKAGES: list = []
|
|
36
|
+
|
|
37
|
+
# Whether to append trailing slashes to URLs.
|
|
38
|
+
APPEND_SLASH = True
|
|
39
|
+
|
|
40
|
+
# A secret key for this particular Plain installation. Used in secret-key
|
|
41
|
+
# hashing algorithms. Set this in your settings, or Plain will complain
|
|
42
|
+
# loudly.
|
|
43
|
+
SECRET_KEY: str
|
|
44
|
+
|
|
45
|
+
# List of secret keys used to verify the validity of signatures. This allows
|
|
46
|
+
# secret key rotation.
|
|
47
|
+
SECRET_KEY_FALLBACKS: list[str] = []
|
|
48
|
+
|
|
49
|
+
ROOT_URLCONF = "urls"
|
|
50
|
+
|
|
51
|
+
# List of upload handler classes to be applied in order.
|
|
52
|
+
FILE_UPLOAD_HANDLERS = [
|
|
53
|
+
"plain.internal.files.uploadhandler.MemoryFileUploadHandler",
|
|
54
|
+
"plain.internal.files.uploadhandler.TemporaryFileUploadHandler",
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
# Maximum size, in bytes, of a request before it will be streamed to the
|
|
58
|
+
# file system instead of into memory.
|
|
59
|
+
FILE_UPLOAD_MAX_MEMORY_SIZE = 2621440 # i.e. 2.5 MB
|
|
60
|
+
|
|
61
|
+
# Maximum size in bytes of request data (excluding file uploads) that will be
|
|
62
|
+
# read before a SuspiciousOperation (RequestDataTooBig) is raised.
|
|
63
|
+
DATA_UPLOAD_MAX_MEMORY_SIZE = 2621440 # i.e. 2.5 MB
|
|
64
|
+
|
|
65
|
+
# Maximum number of GET/POST parameters that will be read before a
|
|
66
|
+
# SuspiciousOperation (TooManyFieldsSent) is raised.
|
|
67
|
+
DATA_UPLOAD_MAX_NUMBER_FIELDS = 1000
|
|
68
|
+
|
|
69
|
+
# Maximum number of files encoded in a multipart upload that will be read
|
|
70
|
+
# before a SuspiciousOperation (TooManyFilesSent) is raised.
|
|
71
|
+
DATA_UPLOAD_MAX_NUMBER_FILES = 100
|
|
72
|
+
|
|
73
|
+
# Directory in which upload streamed files will be temporarily saved. A value of
|
|
74
|
+
# `None` will make Plain use the operating system's default temporary directory
|
|
75
|
+
# (i.e. "/tmp" on *nix systems).
|
|
76
|
+
FILE_UPLOAD_TEMP_DIR = None
|
|
77
|
+
|
|
78
|
+
# The numeric mode to set newly-uploaded files to. The value should be a mode
|
|
79
|
+
# you'd pass directly to os.chmod; see
|
|
80
|
+
# https://docs.python.org/library/os.html#files-and-directories.
|
|
81
|
+
FILE_UPLOAD_PERMISSIONS = 0o644
|
|
82
|
+
|
|
83
|
+
# The numeric mode to assign to newly-created directories, when uploading files.
|
|
84
|
+
# The value should be a mode as you'd pass to os.chmod;
|
|
85
|
+
# see https://docs.python.org/library/os.html#files-and-directories.
|
|
86
|
+
FILE_UPLOAD_DIRECTORY_PERMISSIONS = None
|
|
87
|
+
|
|
88
|
+
# Default X-Frame-Options header value
|
|
89
|
+
X_FRAME_OPTIONS = "DENY"
|
|
90
|
+
|
|
91
|
+
USE_X_FORWARDED_HOST = False
|
|
92
|
+
USE_X_FORWARDED_PORT = False
|
|
93
|
+
|
|
94
|
+
# User-defined overrides for error views by status code
|
|
95
|
+
HTTP_ERROR_VIEWS: dict[int] = {}
|
|
96
|
+
|
|
97
|
+
# If your Plain app is behind a proxy that sets a header to specify secure
|
|
98
|
+
# connections, AND that proxy ensures that user-submitted headers with the
|
|
99
|
+
# same name are ignored (so that people can't spoof it), set this value to
|
|
100
|
+
# a tuple of (header_name, header_value). For any requests that come in with
|
|
101
|
+
# that header/value, request.is_secure() will return True.
|
|
102
|
+
# WARNING! Only set this if you fully understand what you're doing. Otherwise,
|
|
103
|
+
# you may be opening yourself up to a security risk.
|
|
104
|
+
SECURE_PROXY_SSL_HEADER = None
|
|
105
|
+
|
|
106
|
+
##############
|
|
107
|
+
# MIDDLEWARE #
|
|
108
|
+
##############
|
|
109
|
+
|
|
110
|
+
# List of middleware to use. Order is important; in the request phase, these
|
|
111
|
+
# middleware will be applied in the order given, and in the response
|
|
112
|
+
# phase the middleware will be applied in reverse order.
|
|
113
|
+
MIDDLEWARE = [
|
|
114
|
+
"plain.middleware.security.SecurityMiddleware",
|
|
115
|
+
"plain.assets.whitenoise.middleware.WhiteNoiseMiddleware",
|
|
116
|
+
"plain.middleware.common.CommonMiddleware",
|
|
117
|
+
"plain.csrf.middleware.CsrfViewMiddleware",
|
|
118
|
+
"plain.middleware.clickjacking.XFrameOptionsMiddleware",
|
|
119
|
+
]
|
|
120
|
+
|
|
121
|
+
###########
|
|
122
|
+
# SIGNING #
|
|
123
|
+
###########
|
|
124
|
+
|
|
125
|
+
SIGNING_BACKEND = "plain.signing.TimestampSigner"
|
|
126
|
+
|
|
127
|
+
########
|
|
128
|
+
# CSRF #
|
|
129
|
+
########
|
|
130
|
+
|
|
131
|
+
# Settings for CSRF cookie.
|
|
132
|
+
CSRF_COOKIE_NAME = "csrftoken"
|
|
133
|
+
CSRF_COOKIE_AGE = 60 * 60 * 24 * 7 * 52
|
|
134
|
+
CSRF_COOKIE_DOMAIN = None
|
|
135
|
+
CSRF_COOKIE_PATH = "/"
|
|
136
|
+
CSRF_COOKIE_SECURE = False
|
|
137
|
+
CSRF_COOKIE_HTTPONLY = False
|
|
138
|
+
CSRF_COOKIE_SAMESITE = "Lax"
|
|
139
|
+
CSRF_HEADER_NAME = "HTTP_X_CSRFTOKEN"
|
|
140
|
+
CSRF_TRUSTED_ORIGINS: list[str] = []
|
|
141
|
+
CSRF_USE_SESSIONS = False
|
|
142
|
+
|
|
143
|
+
###########
|
|
144
|
+
# LOGGING #
|
|
145
|
+
###########
|
|
146
|
+
|
|
147
|
+
# Custom logging configuration.
|
|
148
|
+
LOGGING = {}
|
|
149
|
+
|
|
150
|
+
###############
|
|
151
|
+
# ASSETS #
|
|
152
|
+
###############
|
|
153
|
+
|
|
154
|
+
ASSETS_BACKEND = "plain.assets.whitenoise.storage.CompressedManifestStaticFilesStorage"
|
|
155
|
+
|
|
156
|
+
# List of finder classes that know how to find assets files in
|
|
157
|
+
# various locations.
|
|
158
|
+
ASSETS_FINDERS = [
|
|
159
|
+
"plain.assets.finders.FileSystemFinder",
|
|
160
|
+
"plain.assets.finders.PackageDirectoriesFinder",
|
|
161
|
+
]
|
|
162
|
+
|
|
163
|
+
# Absolute path to the directory assets files should be collected to.
|
|
164
|
+
# Example: "/var/www/example.com/assets/"
|
|
165
|
+
ASSETS_ROOT = PLAIN_TEMP_PATH / "assets_collected"
|
|
166
|
+
|
|
167
|
+
# URL that handles the assets files served from ASSETS_ROOT.
|
|
168
|
+
# Example: "http://example.com/assets/", "http://assets.example.com/"
|
|
169
|
+
ASSETS_URL = "/assets/"
|
|
170
|
+
|
|
171
|
+
####################
|
|
172
|
+
# PREFLIGHT CHECKS #
|
|
173
|
+
####################
|
|
174
|
+
|
|
175
|
+
# List of all issues generated by system checks that should be silenced. Light
|
|
176
|
+
# issues like warnings, infos or debugs will not generate a message. Silencing
|
|
177
|
+
# serious issues like errors and criticals does not result in hiding the
|
|
178
|
+
# message, but Plain will not stop you from e.g. running server.
|
|
179
|
+
SILENCED_PREFLIGHT_CHECKS = []
|
|
180
|
+
|
|
181
|
+
#######################
|
|
182
|
+
# SECURITY MIDDLEWARE #
|
|
183
|
+
#######################
|
|
184
|
+
SECURE_CONTENT_TYPE_NOSNIFF = True
|
|
185
|
+
SECURE_CROSS_ORIGIN_OPENER_POLICY = "same-origin"
|
|
186
|
+
SECURE_HSTS_INCLUDE_SUBDOMAINS = False
|
|
187
|
+
SECURE_HSTS_PRELOAD = False
|
|
188
|
+
SECURE_HSTS_SECONDS = 0
|
|
189
|
+
SECURE_REDIRECT_EXEMPT = []
|
|
190
|
+
SECURE_REFERRER_POLICY = "same-origin"
|
|
191
|
+
SECURE_SSL_HOST = None
|
|
192
|
+
SECURE_SSL_REDIRECT = False
|
|
193
|
+
|
|
194
|
+
#############
|
|
195
|
+
# Templates #
|
|
196
|
+
#############
|
|
197
|
+
|
|
198
|
+
JINJA_LOADER = "jinja2.loaders.FileSystemLoader"
|
|
199
|
+
JINJA_ENVIRONMENT = "plain.templates.jinja.defaults.create_default_environment"
|