Created
December 23, 2019 19:34
-
-
Save theholy7/08f92a4902936f0efc41dcd4b01337a7 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class AuthorizedResource(Resource): | |
""" | |
We created this AuthorizedResource from Resource | |
because the user authorization was being made after payload | |
validation. This way a non-auth'ed user was able to sniff the | |
payload that was required by the endpoint. | |
By wrapping dispatch_request with the requires_auth function, | |
we check for user authentication first. | |
""" | |
@requires_auth | |
def dispatch_request(self, *args, **kwargs): | |
return super().dispatch_request(*args, **kwargs) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def dispatch_request(self, *args, **kwargs): | |
# Taken from flask | |
meth = getattr(self, request.method.lower(), None) | |
if meth is None and request.method == 'HEAD': | |
meth = getattr(self, 'get', None) | |
assert meth is not None, 'Unimplemented method %r' % request.method | |
for decorator in self.method_decorators: | |
meth = decorator(meth) | |
self.validate_payload(meth) | |
resp = meth(*args, **kwargs) | |
if isinstance(resp, BaseResponse): | |
return resp | |
representations = self.representations or {} | |
mediatype = request.accept_mimetypes.best_match(representations, default=None) | |
if mediatype in representations: | |
data, code, headers = unpack(resp) | |
resp = representations[mediatype](data, code, headers) | |
resp.headers['Content-Type'] = mediatype | |
return resp | |
return resp |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def validate(self, data, resolver=None, format_checker=None): | |
""" | |
Override the parent class validate method to raise custom errors | |
in our API when payloads don't follow the schema rules. | |
This collects a list of errors and delivers it to the user so one | |
request is enough to get all the errors. | |
""" | |
validator = Draft4Validator(self.__schema__, resolver=resolver, format_checker=format_checker) | |
type_errors = [err for err in validator.iter_errors(data)] | |
if len(type_errors) >= 1: | |
# Raise for multiple errors | |
exceptions = [self._convert_type_error_to_custom(err) for err in type_errors] | |
raise MultipleErrorException(exceptions) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from flask_restplus import Model | |
from jsonschema import Draft4Validator | |
class ValidationModel(Model): | |
""" | |
Extend the Model class provided by flask-restplus so that we can add the | |
additionalProperties key to the schema, thus preventing additional properties to | |
be sent in the payload. | |
""" | |
def __init__(self, *args, **kwargs): | |
# pop strict, otherwise it will end up in __apidoc__ and things crash | |
self.strict = kwargs.pop('strict', False) | |
super().__init__(*args, **kwargs) | |
@property | |
def _schema(self): | |
""" | |
Get the base schema from the parent class which handles all the logic | |
to convert the fields into json schemas, and add `additionalProperties` | |
to the schema of the current object, to prevent new properties | |
from being passed in the payload. | |
:returns dict: dictionary containing schema | |
""" | |
base_schema = super()._schema | |
if self.strict: | |
base_schema['additionalProperties'] = False | |
return base_schema | |
def __deepcopy__(self, memo): | |
""" | |
If you create a new class you NEED | |
to overwrite this method to take new parameters you | |
need in it. | |
Otherwise things will break, not exactly sure why. | |
""" | |
obj = self.__class__(self.name, | |
[(key, copy.deepcopy(value, memo)) for key, value in self.items()], | |
mask=self.__mask__, strict=self.strict) | |
obj.__parents__ = self.__parents__ | |
return obj | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class ValidationNamespace(Namespace): | |
""" | |
Validation Namespace extends the normal Namespace class to | |
overwrite the model function, so that we can register a ValidationModel | |
by adding directly to the namespace with `ns.model(...)`. | |
""" | |
def model(self, name=None, model=None, mask=None, **kwargs): | |
""" | |
Register a validation model so that classes created with the model method | |
can receive the `strict` kwarg. | |
""" | |
model = ValidationModel(name, model, mask=mask, **kwargs) | |
model.__apidoc__.update(kwargs) | |
return self.add_model(name, model) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment