-
Notifications
You must be signed in to change notification settings - Fork 2
API based allow list script #223
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| { | ||
| "$schema": "https://json-schema.org/draft/2020-12/schema", | ||
| "title": "AllowRule", | ||
|
|
||
| "type": "object", | ||
| "additionalProperties": false, | ||
| "required": ["data"], | ||
|
|
||
| "properties": { | ||
| "data": { | ||
| "type": "object", | ||
| "additionalProperties": false, | ||
| "required": ["type", "meta"], | ||
|
|
||
| "properties": { | ||
| "type": {"enum": ["AllowRule"]}, | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The type is mandatory for JSON:API. I chose the class name as the name for the type as:
|
||
|
|
||
| "meta": { | ||
| "type": "object", | ||
| "additionalProperties": false, | ||
| "required": ["url"], | ||
|
|
||
| "properties": { | ||
| "url": {"type": "string", "format": "public-url"} | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This is a custom format defined by us. It's totally valid to put whatever you like here in JSON schema. There are a few default formats understood by all standard JSON schema validators, but any they don't understand are just ignored.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I decided to make the
|
||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -20,6 +20,8 @@ def add_routes(config): | |
| config.add_route("login_callback", "/ui/api/login_callback") | ||
| config.add_route("logout", "/ui/api/logout") | ||
|
|
||
| config.add_route("add_to_allow_list", "/ui/api/rule", request_method="POST") | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this should probably be This'd also mean moving |
||
|
|
||
|
|
||
| def includeme(config): # pragma: no cover | ||
| """Pyramid config.""" | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,9 @@ | ||
| <style> | ||
| code { | ||
| word-break: break-all; | ||
| } | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The command gets very long with the session JWT, so enable word wrapping so we can copy with without scrolling horizontally. |
||
| </style> | ||
|
|
||
| <h1>Hello {{ request.session.user.name }}</h1> | ||
|
|
||
| {% set user = request.session.user %} | ||
|
|
@@ -13,5 +19,10 @@ | |
| <h2>Session</h2> | ||
| <code>{{ request.session }}</code> | ||
|
|
||
| <h2>Add to allow list</h2> | ||
|
|
||
|
|
||
| <code>tox -qe dev --run-command "python bin/add_to_allow_list.py --session={{ session }} --route={{ request.route_url('add_to_allow_list') }}"</code> | ||
|
|
||
| <hr> | ||
| <a href="{{ request.route_url("logout") }}">Logout</a> | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| """Validation tools for working with jsonschema.""" | ||
|
|
||
| import json | ||
|
|
||
| from jsonschema import Draft7Validator, FormatChecker, ValidationError | ||
| from pkg_resources import resource_stream | ||
|
|
||
| from checkmate.exceptions import MalformedJSONBody | ||
| from checkmate.url import CanonicalURL, Domain | ||
|
|
||
| _FORMAT_CHECKER = FormatChecker() | ||
|
|
||
|
|
||
| @_FORMAT_CHECKER.checks("public-url", raises=(ValueError,)) | ||
| def _check_public_url(instance): | ||
| """A validator which checks that a given URL is publically available. | ||
|
|
||
| This only uses static data, not an actual check online. | ||
| """ | ||
| _, netloc, _, _, _, _ = CanonicalURL.canonical_split(instance) | ||
| domain = Domain(netloc) | ||
| if not domain.is_valid: | ||
| raise ValueError("The URL does not have a valid domain") | ||
|
|
||
| if not domain.is_public: | ||
| raise ValueError("The URL is not public") | ||
|
|
||
| return True | ||
|
|
||
|
|
||
| def get_validator(schema_path): | ||
| """Get a jsonschema validator object for a given schema path. | ||
|
|
||
| :param schema_path: Path relative to the checkmate root | ||
| """ | ||
|
|
||
| return Draft7Validator( | ||
| json.load(resource_stream("checkmate", schema_path)), | ||
| format_checker=_FORMAT_CHECKER, | ||
| ) | ||
|
|
||
|
|
||
| def get_validated_json_body(request, validator): | ||
| """Get the JSON body of a request validated against a jsonschema | ||
|
|
||
| :param request: Pyramid request object | ||
| :param validator: A jsonschema validator (see `get_validator()`) | ||
| :return: The json dict if validation is successful | ||
|
|
||
| :raise MalformedJSONBody: If the JSON cannot be decoded or the body | ||
| does not conform to the schema provided | ||
| """ | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm sure we could do a fancy decorator like we do in
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think we need to do it but a lighter weight nicety could be to add this as a request method: |
||
|
|
||
| try: | ||
| body = request.json_body | ||
| except ValueError as err: | ||
| raise MalformedJSONBody(f"Posted JSON missing or malformed: {err}") from err | ||
|
|
||
| try: | ||
| validator.validate(body) | ||
| except ValidationError as err: | ||
| raise MalformedJSONBody( | ||
| f"JSON body does not match expected schema: {err}" | ||
| ) from err | ||
|
|
||
| return body | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't flexible here, and I think it shouldn't be unless it has to be. Having every object have it's primary key as
idis a very handy convention.