add more validation rules

This commit is contained in:
2023-02-23 14:48:22 +02:00
parent 4fb9335df9
commit 13faf66bdb
12 changed files with 165 additions and 54 deletions

View File

@ -52,7 +52,7 @@ class Validate(Handler):
unsafe(bool): if set no user check will be performed before path creation
"""
schema = Validate.schema(architecture, configuration)
validator = Validator(instance=configuration, schema=schema)
validator = Validator(configuration=configuration, schema=schema)
if validator.validate(configuration.dump()):
return # no errors found

View File

@ -64,6 +64,7 @@ CONFIGURATION_SCHEMA: ConfigurationSchema = {
"mirror": {
"type": "string",
"required": True,
"is_url": [],
},
"repositories": {
"type": "list",
@ -111,10 +112,13 @@ CONFIGURATION_SCHEMA: ConfigurationSchema = {
},
"cookie_secret_key": {
"type": "string",
"minlength": 32,
"maxlength": 64, # we cannot verify maxlength, because base64 representation might be longer than bytes
},
"max_age": {
"type": "integer",
"coerce": "integer",
"min": 0,
},
"oauth_provider": {
"type": "string",
@ -162,6 +166,7 @@ CONFIGURATION_SCHEMA: ConfigurationSchema = {
"vcs_allowed_age": {
"type": "integer",
"coerce": "integer",
"min": 0,
},
},
},
@ -204,6 +209,7 @@ CONFIGURATION_SCHEMA: ConfigurationSchema = {
"schema": {
"address": {
"type": "string",
"is_url": ["http", "https"],
},
"debug": {
"type": "boolean",
@ -220,9 +226,11 @@ CONFIGURATION_SCHEMA: ConfigurationSchema = {
},
"host": {
"type": "string",
"is_ip_address": ["localhost"],
},
"index_url": {
"type": "string",
"is_url": ["http", "https"],
},
"password": {
"type": "string",
@ -258,44 +266,4 @@ CONFIGURATION_SCHEMA: ConfigurationSchema = {
},
},
},
"remote-pull": {
"type": "dict",
"schema": {
"target": {
"type": "list",
"coerce": "list",
"schema": {"type": "string"},
},
},
},
"remote-push": {
"type": "dict",
"schema": {
"target": {
"type": "list",
"coerce": "list",
"schema": {"type": "string"},
},
},
},
"report": {
"type": "dict",
"schema": {
"target": {
"type": "list",
"coerce": "list",
"schema": {"type": "string"},
},
},
},
"upload": {
"type": "dict",
"schema": {
"target": {
"type": "list",
"coerce": "list",
"schema": {"type": "string"},
},
},
},
}

View File

@ -17,9 +17,12 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
import ipaddress
from cerberus import TypeDefinition, Validator as RootValidator # type: ignore
from pathlib import Path
from typing import Any, List
from urllib.parse import urlparse
from ahriman.core.configuration import Configuration
@ -29,7 +32,7 @@ class Validator(RootValidator): # type: ignore
class which defines custom validation methods for the service configuration
Attributes:
instance(Configuration): configuration instance
configuration(Configuration): configuration instance
"""
types_mapping = RootValidator.types_mapping.copy()
@ -40,12 +43,12 @@ class Validator(RootValidator): # type: ignore
default constructor
Args:
instance(Configuration): configuration instance used for extraction
configuration(Configuration): configuration instance used for extraction
*args(Any): positional arguments to be passed to base validator
**kwargs(): keyword arguments to be passed to base validator
"""
RootValidator.__init__(self, *args, **kwargs)
self.instance: Configuration = kwargs["instance"]
self.configuration: Configuration = kwargs["configuration"]
def _normalize_coerce_absolute_path(self, value: str) -> Path:
"""
@ -57,7 +60,7 @@ class Validator(RootValidator): # type: ignore
Returns:
Path: value converted to path instance according to configuration rules
"""
converted: Path = self.instance.converters["path"](value)
converted: Path = self.configuration.converters["path"](value)
return converted
def _normalize_coerce_boolean(self, value: str) -> bool:
@ -71,7 +74,7 @@ class Validator(RootValidator): # type: ignore
bool: value converted to boolean according to configuration rules
"""
# pylint: disable=protected-access
converted: bool = self.instance._convert_to_boolean(value) # type: ignore
converted: bool = self.configuration._convert_to_boolean(value) # type: ignore
return converted
def _normalize_coerce_integer(self, value: str) -> int:
@ -97,9 +100,50 @@ class Validator(RootValidator): # type: ignore
Returns:
List[str]: value converted to string list instance according to configuration rules
"""
converted: List[str] = self.instance.converters["list"](value)
converted: List[str] = self.configuration.converters["list"](value)
return converted
def _validate_is_ip_address(self, constraint: List[str], field: str, value: str) -> None:
"""
check if the specified value is valid ip address
Args:
constraint(List[str]): optional list of allowed special words (e.g. ``localhost``)
field(str): field name to be checked
value(Path): value to be checked
Examples:
The rule's arguments are validated against this schema:
{"type": "list", "schema": {"type": "string"}}
"""
if value in constraint:
return
try:
ipaddress.ip_address(value)
except ValueError:
self._error(field, f"Value {value} must be valid IP address")
def _validate_is_url(self, constraint: List[str], field: str, value: str) -> None:
"""
check if the specified value is a valid url
Args:
constraint(List[str]): optional list of supported schemas. If empty, no schema validation will be performed
field(str): field name to be checked
value(str): value to be checked
Examples:
The rule's arguments are validated against this schema:
{"type": "list", "schema": {"type": "string"}}
"""
url = urlparse(value) # it probably will never rise exceptions on parse
if not url.scheme:
self._error(field, f"Url scheme is not set for {value}")
if not url.netloc and url.scheme not in ("file",):
self._error(field, f"Location must be set for url {value} of scheme {url.scheme}")
if constraint and url.scheme not in constraint:
self._error(field, f"Url {value} scheme must be one of {constraint}")
def _validate_path_exists(self, constraint: bool, field: str, value: Path) -> None:
"""
check if paths exists

View File

@ -33,6 +33,16 @@ class RemotePullTrigger(Trigger):
"""
CONFIGURATION_SCHEMA = {
"remote-pull": {
"type": "dict",
"schema": {
"target": {
"type": "list",
"coerce": "list",
"schema": {"type": "string"},
},
},
},
"gitremote": {
"type": "dict",
"schema": {

View File

@ -38,6 +38,16 @@ class RemotePushTrigger(Trigger):
"""
CONFIGURATION_SCHEMA = {
"remote-push": {
"type": "dict",
"schema": {
"target": {
"type": "list",
"coerce": "list",
"schema": {"type": "string"},
},
},
},
"gitremote": {
"type": "dict",
"schema": {

View File

@ -35,6 +35,16 @@ class ReportTrigger(Trigger):
"""
CONFIGURATION_SCHEMA = {
"report": {
"type": "dict",
"schema": {
"target": {
"type": "list",
"coerce": "list",
"schema": {"type": "string"},
},
},
},
"console": {
"type": "dict",
"schema": {
@ -62,6 +72,7 @@ class ReportTrigger(Trigger):
},
"homepage": {
"type": "string",
"is_url": ["http", "https"],
},
"host": {
"type": "string",
@ -70,6 +81,7 @@ class ReportTrigger(Trigger):
"link_path": {
"type": "string",
"required": True,
"is_url": [],
},
"no_empty_report": {
"type": "boolean",
@ -82,6 +94,8 @@ class ReportTrigger(Trigger):
"type": "integer",
"coerce": "integer",
"required": True,
"min": 0,
"max": 65535,
},
"receivers": {
"type": "list",
@ -118,10 +132,12 @@ class ReportTrigger(Trigger):
},
"homepage": {
"type": "string",
"is_url": ["http", "https"],
},
"link_path": {
"type": "string",
"required": True,
"is_url": [],
},
"path": {
"type": "path",
@ -153,10 +169,12 @@ class ReportTrigger(Trigger):
},
"homepage": {
"type": "string",
"is_url": ["http", "https"],
},
"link_path": {
"type": "string",
"required": True,
"is_url": [],
},
"template_path": {
"type": "path",
@ -171,6 +189,7 @@ class ReportTrigger(Trigger):
"timeout": {
"type": "integer",
"coerce": "integer",
"min": 0,
},
},
},

View File

@ -35,6 +35,16 @@ class UploadTrigger(Trigger):
"""
CONFIGURATION_SCHEMA = {
"upload": {
"type": "dict",
"schema": {
"target": {
"type": "list",
"coerce": "list",
"schema": {"type": "string"},
},
},
},
"github": {
"type": "dict",
"schema": {
@ -57,6 +67,7 @@ class UploadTrigger(Trigger):
"timeout": {
"type": "integer",
"coerce": "integer",
"min": 0,
},
"username": {
"type": "string",
@ -101,6 +112,7 @@ class UploadTrigger(Trigger):
"chunk_size": {
"type": "integer",
"coerce": "integer",
"min": 0,
},
"region": {
"type": "string",