new option for checking path/user value
This commit is contained in:
@@ -1617,6 +1617,33 @@ Strict preconditions check on PUT in case item already exists [RFC6352#9.2](http
|
|||||||
|
|
||||||
Default: `False`
|
Default: `False`
|
||||||
|
|
||||||
|
##### validate_user_value
|
||||||
|
|
||||||
|
_(>= 3.7.2)_
|
||||||
|
|
||||||
|
Validate user value content
|
||||||
|
|
||||||
|
Available types are:
|
||||||
|
* `none`
|
||||||
|
* `minimal` (control and some special chars)
|
||||||
|
* `unicodeletter` (unicode letters)
|
||||||
|
* `strict` (reduced ASCII set)
|
||||||
|
|
||||||
|
Default: `minimum`
|
||||||
|
|
||||||
|
##### validate_path_type
|
||||||
|
|
||||||
|
_(>= 3.7.2)_
|
||||||
|
|
||||||
|
Validate path value content
|
||||||
|
|
||||||
|
* `none`
|
||||||
|
* `minimal` (control and some special chars)
|
||||||
|
* `unicodeletter` (unicode letters)
|
||||||
|
* `strict` (reduced ASCII set)
|
||||||
|
|
||||||
|
Default: `minimum`
|
||||||
|
|
||||||
##### hook
|
##### hook
|
||||||
|
|
||||||
Command that is run after changes to storage. See the
|
Command that is run after changes to storage. See the
|
||||||
|
|||||||
8
config
8
config
@@ -58,6 +58,14 @@
|
|||||||
# script name to strip from URI if called by reverse proxy
|
# script name to strip from URI if called by reverse proxy
|
||||||
#script_name = (default taken from HTTP_X_SCRIPT_NAME or SCRIPT_NAME)
|
#script_name = (default taken from HTTP_X_SCRIPT_NAME or SCRIPT_NAME)
|
||||||
|
|
||||||
|
# validate user type
|
||||||
|
# Value: none|minimal|unicodeletter|strict
|
||||||
|
#validate_user_type = minimal
|
||||||
|
|
||||||
|
# validate path value
|
||||||
|
# Value: none|minimal|unicodeletter|strict
|
||||||
|
#validate_path_type = minimal
|
||||||
|
|
||||||
|
|
||||||
[encoding]
|
[encoding]
|
||||||
|
|
||||||
|
|||||||
@@ -86,6 +86,8 @@ class Application(ApplicationPartDelete, ApplicationPartHead,
|
|||||||
_profiling_per_request: bool = False
|
_profiling_per_request: bool = False
|
||||||
_profiling_per_request_method: bool = False
|
_profiling_per_request_method: bool = False
|
||||||
_limit_content: int
|
_limit_content: int
|
||||||
|
_validate_user_value: str
|
||||||
|
_validate_path_value: str
|
||||||
profiler_per_request_method: dict[str, cProfile.Profile] = {}
|
profiler_per_request_method: dict[str, cProfile.Profile] = {}
|
||||||
profiler_per_request_method_counter: dict[str, int] = {}
|
profiler_per_request_method_counter: dict[str, int] = {}
|
||||||
profiler_per_request_method_starttime: datetime.datetime
|
profiler_per_request_method_starttime: datetime.datetime
|
||||||
@@ -158,6 +160,11 @@ class Application(ApplicationPartDelete, ApplicationPartHead,
|
|||||||
self._extra_headers[key] = configuration.get("headers", key)
|
self._extra_headers[key] = configuration.get("headers", key)
|
||||||
self._strict_preconditions = configuration.get("storage", "strict_preconditions")
|
self._strict_preconditions = configuration.get("storage", "strict_preconditions")
|
||||||
logger.info("strict preconditions check: %s", self._strict_preconditions)
|
logger.info("strict preconditions check: %s", self._strict_preconditions)
|
||||||
|
# Format checks
|
||||||
|
self._validate_user_value = configuration.get("server", "validate_user_value")
|
||||||
|
self._validate_path_value = configuration.get("server", "validate_path_value")
|
||||||
|
logger.info("validate user value: %r", self._validate_user_value)
|
||||||
|
logger.info("validate path value: %r", self._validate_path_value)
|
||||||
# Profiling options
|
# Profiling options
|
||||||
self._profiling = configuration.get("logging", "profiling")
|
self._profiling = configuration.get("logging", "profiling")
|
||||||
self._profiling_per_request_min_duration = configuration.get("logging", "profiling_per_request_min_duration")
|
self._profiling_per_request_min_duration = configuration.get("logging", "profiling_per_request_min_duration")
|
||||||
|
|||||||
@@ -44,6 +44,8 @@ class ApplicationBase:
|
|||||||
_permit_delete_collection: bool
|
_permit_delete_collection: bool
|
||||||
_permit_overwrite_collection: bool
|
_permit_overwrite_collection: bool
|
||||||
_strict_preconditions: bool
|
_strict_preconditions: bool
|
||||||
|
_validate_user_value: str
|
||||||
|
_validate_path_format: str
|
||||||
_hook: hook.BaseHook
|
_hook: hook.BaseHook
|
||||||
|
|
||||||
def __init__(self, configuration: config.Configuration) -> None:
|
def __init__(self, configuration: config.Configuration) -> None:
|
||||||
@@ -58,6 +60,8 @@ class ApplicationBase:
|
|||||||
self._response_content_on_debug = configuration.get("logging", "response_content_on_debug")
|
self._response_content_on_debug = configuration.get("logging", "response_content_on_debug")
|
||||||
self._request_content_on_debug = configuration.get("logging", "request_content_on_debug")
|
self._request_content_on_debug = configuration.get("logging", "request_content_on_debug")
|
||||||
self._limit_content = configuration.get("logging", "limit_content")
|
self._limit_content = configuration.get("logging", "limit_content")
|
||||||
|
self._validate_user_value = configuration.get("server", "validate_user_value")
|
||||||
|
self._validate_path_value = configuration.get("server", "validate_path_value")
|
||||||
self._hook = hook.load(configuration)
|
self._hook = hook.load(configuration)
|
||||||
|
|
||||||
def _read_xml_request_body(self, environ: types.WSGIEnviron
|
def _read_xml_request_body(self, environ: types.WSGIEnviron
|
||||||
|
|||||||
@@ -48,6 +48,8 @@ DEFAULT_CONFIG_PATH: str = os.pathsep.join([
|
|||||||
|
|
||||||
PROFILING: Sequence[str] = ("per_request", "per_request_method", "none")
|
PROFILING: Sequence[str] = ("per_request", "per_request_method", "none")
|
||||||
|
|
||||||
|
VALIDATE_TYPES: Sequence[str] = ("none", "minimal", "unicodeletter", "strict")
|
||||||
|
|
||||||
|
|
||||||
def positive_int(value: Any) -> int:
|
def positive_int(value: Any) -> int:
|
||||||
value = int(value)
|
value = int(value)
|
||||||
@@ -84,6 +86,12 @@ def logging_level(value: Any) -> str:
|
|||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def validate_types(value: Any) -> str:
|
||||||
|
if value not in VALIDATE_TYPES:
|
||||||
|
raise ValueError("unsupported validation type: %r" % value)
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
def profiling(value: Any) -> str:
|
def profiling(value: Any) -> str:
|
||||||
if value not in PROFILING:
|
if value not in PROFILING:
|
||||||
raise ValueError("unsupported profiling: %r" % value)
|
raise ValueError("unsupported profiling: %r" % value)
|
||||||
@@ -221,6 +229,14 @@ DEFAULT_CONFIG_SCHEMA: types.CONFIG_SCHEMA = OrderedDict([
|
|||||||
"value": "",
|
"value": "",
|
||||||
"help": "script name to strip from URI if called by reverse proxy (default taken from HTTP_X_SCRIPT_NAME or SCRIPT_NAME)",
|
"help": "script name to strip from URI if called by reverse proxy (default taken from HTTP_X_SCRIPT_NAME or SCRIPT_NAME)",
|
||||||
"type": str}),
|
"type": str}),
|
||||||
|
("validate_user_value", {
|
||||||
|
"value": "minimal",
|
||||||
|
"help": "validate user value (" + "|".join(VALIDATE_TYPES) + ")",
|
||||||
|
"type": validate_types}),
|
||||||
|
("validate_path_value", {
|
||||||
|
"value": "minimal",
|
||||||
|
"help": "validate path value (" + "|".join(VALIDATE_TYPES) + ")",
|
||||||
|
"type": validate_types}),
|
||||||
("_internal_server", {
|
("_internal_server", {
|
||||||
"value": "False",
|
"value": "False",
|
||||||
"help": "the internal server is used",
|
"help": "the internal server is used",
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ from urllib.parse import parse_qs
|
|||||||
|
|
||||||
from radicale import (config, httputils, pathutils, rights, storage, types,
|
from radicale import (config, httputils, pathutils, rights, storage, types,
|
||||||
utils)
|
utils)
|
||||||
|
from radicale.app.base import ApplicationBase
|
||||||
from radicale.log import logger
|
from radicale.log import logger
|
||||||
|
|
||||||
INTERNAL_TYPES: Sequence[str] = ("csv", "files", "none")
|
INTERNAL_TYPES: Sequence[str] = ("csv", "files", "none")
|
||||||
@@ -135,7 +136,7 @@ def load(configuration: "config.Configuration") -> "BaseSharing":
|
|||||||
return utils.load_plugin(INTERNAL_TYPES, "sharing", "Sharing", BaseSharing, configuration)
|
return utils.load_plugin(INTERNAL_TYPES, "sharing", "Sharing", BaseSharing, configuration)
|
||||||
|
|
||||||
|
|
||||||
class BaseSharing:
|
class BaseSharing(ApplicationBase):
|
||||||
|
|
||||||
_storage: storage.BaseStorage
|
_storage: storage.BaseStorage
|
||||||
_rights: rights.BaseRights
|
_rights: rights.BaseRights
|
||||||
@@ -157,6 +158,8 @@ class BaseSharing:
|
|||||||
self._rights = rights.load(configuration)
|
self._rights = rights.load(configuration)
|
||||||
self._storage = storage.load(configuration)
|
self._storage = storage.load(configuration)
|
||||||
self._auth_delay = configuration.get("auth", "delay")
|
self._auth_delay = configuration.get("auth", "delay")
|
||||||
|
self._validate_user_value = configuration.get("server", "validate_user_value")
|
||||||
|
self._validate_path_value = configuration.get("server", "validate_path_value")
|
||||||
# Sharing
|
# Sharing
|
||||||
self.sharing_collection_by_map = configuration.get("sharing", "collection_by_map")
|
self.sharing_collection_by_map = configuration.get("sharing", "collection_by_map")
|
||||||
self.sharing_collection_by_token = configuration.get("sharing", "collection_by_token")
|
self.sharing_collection_by_token = configuration.get("sharing", "collection_by_token")
|
||||||
|
|||||||
Reference in New Issue
Block a user