Code style, license headers
Apply my common Python code and header style as also found in other projects. Signed-off-by: Georg Pfuetzenreuter <mail@georg-pfuetzenreuter.net>
This commit is contained in:
parent
75c2411dd2
commit
a47ee638f1
@ -1,4 +1,15 @@
|
||||
"""
|
||||
A RESTful HTTP API for nftables
|
||||
Copyright 2024, Georg Pfuetzenreuter <mail@georg-pfuetzenreuter.net>
|
||||
|
||||
Licensed under the EUPL, Version 1.2 or - as soon they will be approved by the European Commission - subsequent versions of the EUPL (the "Licence").
|
||||
You may not use this work except in compliance with the Licence.
|
||||
An English copy of the Licence is shipped in a file called LICENSE along with this applications source code.
|
||||
You may obtain copies of the Licence in any of the official languages at https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12.
|
||||
"""
|
||||
|
||||
from waitress import serve
|
||||
|
||||
from nftables_api.app import app
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
@ -0,0 +1,9 @@
|
||||
"""
|
||||
A RESTful HTTP API for nftables
|
||||
Copyright 2024, Georg Pfuetzenreuter <mail@georg-pfuetzenreuter.net>
|
||||
|
||||
Licensed under the EUPL, Version 1.2 or - as soon they will be approved by the European Commission - subsequent versions of the EUPL (the "Licence").
|
||||
You may not use this work except in compliance with the Licence.
|
||||
An English copy of the Licence is shipped in a file called LICENSE along with this applications source code.
|
||||
You may obtain copies of the Licence in any of the official languages at https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12.
|
||||
"""
|
@ -1,14 +1,25 @@
|
||||
"""
|
||||
A RESTful HTTP API for nftables
|
||||
Copyright 2024, Georg Pfuetzenreuter <mail@georg-pfuetzenreuter.net>
|
||||
|
||||
Licensed under the EUPL, Version 1.2 or - as soon they will be approved by the European Commission - subsequent versions of the EUPL (the "Licence").
|
||||
You may not use this work except in compliance with the Licence.
|
||||
An English copy of the Licence is shipped in a file called LICENSE along with this applications source code.
|
||||
You may obtain copies of the Licence in any of the official languages at https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12.
|
||||
"""
|
||||
|
||||
from falcon import App
|
||||
from .resources import nft_set
|
||||
from .config import config
|
||||
|
||||
from nftables_api.middlewares.authentication import AuthMiddleWare
|
||||
|
||||
from .resources import nft_set
|
||||
|
||||
app = App(
|
||||
middleware=[
|
||||
AuthMiddleWare(),
|
||||
]
|
||||
],
|
||||
)
|
||||
|
||||
rSet = nft_set.SetResource()
|
||||
rset = nft_set.SetResource()
|
||||
|
||||
app.add_route('/set/{xfamily}/{xtable}/{xset}', rSet)
|
||||
app.add_route('/set/{xfamily}/{xtable}/{xset}', rset)
|
||||
|
@ -1,6 +1,17 @@
|
||||
from yaml import safe_load
|
||||
"""
|
||||
A RESTful HTTP API for nftables
|
||||
Copyright 2024, Georg Pfuetzenreuter <mail@georg-pfuetzenreuter.net>
|
||||
|
||||
Licensed under the EUPL, Version 1.2 or - as soon they will be approved by the European Commission - subsequent versions of the EUPL (the "Licence").
|
||||
You may not use this work except in compliance with the Licence.
|
||||
An English copy of the Licence is shipped in a file called LICENSE along with this applications source code.
|
||||
You may obtain copies of the Licence in any of the official languages at https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12.
|
||||
"""
|
||||
|
||||
from os import getenv
|
||||
|
||||
from yaml import safe_load
|
||||
|
||||
configpath = getenv('NFT-API-CONFIG')
|
||||
if not configpath:
|
||||
raise RuntimeError('NFT-API-CONFIG is not set')
|
||||
|
9
nftables_api/middlewares/__init__.py
Normal file
9
nftables_api/middlewares/__init__.py
Normal file
@ -0,0 +1,9 @@
|
||||
"""
|
||||
A RESTful HTTP API for nftables
|
||||
Copyright 2024, Georg Pfuetzenreuter <mail@georg-pfuetzenreuter.net>
|
||||
|
||||
Licensed under the EUPL, Version 1.2 or - as soon they will be approved by the European Commission - subsequent versions of the EUPL (the "Licence").
|
||||
You may not use this work except in compliance with the Licence.
|
||||
An English copy of the Licence is shipped in a file called LICENSE along with this applications source code.
|
||||
You may obtain copies of the Licence in any of the official languages at https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12.
|
||||
"""
|
@ -1,7 +1,19 @@
|
||||
"""
|
||||
A RESTful HTTP API for nftables
|
||||
Copyright 2024, Georg Pfuetzenreuter <mail@georg-pfuetzenreuter.net>
|
||||
|
||||
Licensed under the EUPL, Version 1.2 or - as soon they will be approved by the European Commission - subsequent versions of the EUPL (the "Licence").
|
||||
You may not use this work except in compliance with the Licence.
|
||||
An English copy of the Licence is shipped in a file called LICENSE along with this applications source code.
|
||||
You may obtain copies of the Licence in any of the official languages at https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12.
|
||||
"""
|
||||
|
||||
from bcrypt import checkpw
|
||||
from falcon import HTTPUnauthorized
|
||||
|
||||
from nftables_api.config import config
|
||||
|
||||
|
||||
class AuthMiddleWare:
|
||||
def _match(self, token_plain, token_hashed):
|
||||
"""
|
||||
@ -23,7 +35,7 @@ class AuthMiddleWare:
|
||||
return False
|
||||
|
||||
|
||||
def process_request(self, req, resp):
|
||||
def process_request(self, req, resp): # noqa ARG002, resp is not used but needs to be passed by Falcon
|
||||
"""
|
||||
Rudimentary token validation - check if it is worth walking further down the authorization chain
|
||||
"""
|
||||
@ -40,7 +52,7 @@ class AuthMiddleWare:
|
||||
)
|
||||
|
||||
|
||||
def process_resource(self, req, resp, resource, params):
|
||||
def process_resource(self, req, resp, resource, params): # noqa ARG002, resp is not used but needs to be passed by Falcon
|
||||
"""
|
||||
Fully validate whether a token is authorized to perform the request
|
||||
"""
|
||||
@ -51,13 +63,15 @@ class AuthMiddleWare:
|
||||
if not self._match(token, config_token):
|
||||
continue
|
||||
|
||||
for config_path, methods in config_paths.items():
|
||||
for got_config_path, methods in config_paths.items():
|
||||
if not isinstance(methods, list):
|
||||
raise RuntimeError(f'Invalid method configured for path {config_path}')
|
||||
raise RuntimeError(f'Invalid method configured for path {got_config_path}')
|
||||
|
||||
# a leading slash causes an empty first list entry in the split
|
||||
if config_path.startswith('/'):
|
||||
config_path = config_path[1:]
|
||||
if got_config_path.startswith('/'):
|
||||
config_path = got_config_path[1:]
|
||||
else:
|
||||
config_path = got_config_path
|
||||
|
||||
path_elements = config_path.split('/')
|
||||
|
||||
@ -76,13 +90,13 @@ class AuthMiddleWare:
|
||||
break
|
||||
|
||||
else:
|
||||
if req.method in methods:
|
||||
return
|
||||
else:
|
||||
if req.method not in methods:
|
||||
raise HTTPUnauthorized(
|
||||
title='Unauthorized method for path',
|
||||
)
|
||||
|
||||
return
|
||||
|
||||
raise HTTPUnauthorized(
|
||||
title='Unauthorized',
|
||||
)
|
||||
|
9
nftables_api/resources/__init__.py
Normal file
9
nftables_api/resources/__init__.py
Normal file
@ -0,0 +1,9 @@
|
||||
"""
|
||||
A RESTful HTTP API for nftables
|
||||
Copyright 2024, Georg Pfuetzenreuter <mail@georg-pfuetzenreuter.net>
|
||||
|
||||
Licensed under the EUPL, Version 1.2 or - as soon they will be approved by the European Commission - subsequent versions of the EUPL (the "Licence").
|
||||
You may not use this work except in compliance with the Licence.
|
||||
An English copy of the Licence is shipped in a file called LICENSE along with this applications source code.
|
||||
You may obtain copies of the Licence in any of the official languages at https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12.
|
||||
"""
|
@ -1,8 +1,20 @@
|
||||
import falcon
|
||||
"""
|
||||
A RESTful HTTP API for nftables
|
||||
Copyright 2024, Georg Pfuetzenreuter <mail@georg-pfuetzenreuter.net>
|
||||
|
||||
Licensed under the EUPL, Version 1.2 or - as soon they will be approved by the European Commission - subsequent versions of the EUPL (the "Licence").
|
||||
You may not use this work except in compliance with the Licence.
|
||||
An English copy of the Licence is shipped in a file called LICENSE along with this applications source code.
|
||||
You may obtain copies of the Licence in any of the official languages at https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12.
|
||||
"""
|
||||
|
||||
import json
|
||||
|
||||
import falcon
|
||||
from nftables import Nftables
|
||||
from nftables_api.utils.parse import parse_nft_response
|
||||
|
||||
from nftables_api.utils.output import output_post
|
||||
from nftables_api.utils.parse import parse_nft_response
|
||||
|
||||
nft = Nftables()
|
||||
nft.set_json_output(True)
|
||||
@ -47,7 +59,7 @@ class SetResource:
|
||||
response.text = json.dumps({'status': status, 'error': err_parsed})
|
||||
|
||||
|
||||
def on_post(self, request, response, xfamily, xtable, xset):
|
||||
def on_post(self, request, response, xfamily, xtable, xset): # noqa PLR0912, todo: consider moving some of the logic to smaller functions
|
||||
raw = request.get_param_as_bool('raw', default=False)
|
||||
data = request.get_media()
|
||||
|
||||
@ -73,7 +85,7 @@ class SetResource:
|
||||
'prefix': {
|
||||
'addr': addrsplit[0],
|
||||
'len': int(addrsplit[1]),
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
else:
|
||||
@ -88,19 +100,19 @@ class SetResource:
|
||||
'family': xfamily,
|
||||
'name': xset,
|
||||
'table': xtable,
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
if not nft.json_validate(nft_payload):
|
||||
response.status = falcon.HTTP_BAD_REQUEST
|
||||
response.text = output_post(False, 'Payload did not validate.')
|
||||
response.text = output_post(status=False, message='Payload did not validate.')
|
||||
return
|
||||
|
||||
rc, out, err = nft.json_cmd(nft_payload)
|
||||
out_parsed, status, err_parsed = parse_nft_response(rc, out, err, raw)
|
||||
_, status, err_parsed = parse_nft_response(rc, out, err, raw)
|
||||
|
||||
if status is True:
|
||||
response.status = falcon.HTTP_CREATED
|
||||
|
9
nftables_api/utils/__init__.py
Normal file
9
nftables_api/utils/__init__.py
Normal file
@ -0,0 +1,9 @@
|
||||
"""
|
||||
A RESTful HTTP API for nftables
|
||||
Copyright 2024, Georg Pfuetzenreuter <mail@georg-pfuetzenreuter.net>
|
||||
|
||||
Licensed under the EUPL, Version 1.2 or - as soon they will be approved by the European Commission - subsequent versions of the EUPL (the "Licence").
|
||||
You may not use this work except in compliance with the Licence.
|
||||
An English copy of the Licence is shipped in a file called LICENSE along with this applications source code.
|
||||
You may obtain copies of the Licence in any of the official languages at https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12.
|
||||
"""
|
@ -1,15 +1,26 @@
|
||||
"""
|
||||
A RESTful HTTP API for nftables
|
||||
Copyright 2024, Georg Pfuetzenreuter <mail@georg-pfuetzenreuter.net>
|
||||
|
||||
Licensed under the EUPL, Version 1.2 or - as soon they will be approved by the European Commission - subsequent versions of the EUPL (the "Licence").
|
||||
You may not use this work except in compliance with the Licence.
|
||||
An English copy of the Licence is shipped in a file called LICENSE along with this applications source code.
|
||||
You may obtain copies of the Licence in any of the official languages at https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12.
|
||||
"""
|
||||
|
||||
import json
|
||||
|
||||
|
||||
def output_post(status, message=None):
|
||||
output = {
|
||||
'status': status
|
||||
'status': status,
|
||||
}
|
||||
|
||||
if message:
|
||||
output.update(
|
||||
{
|
||||
'message': message
|
||||
}
|
||||
'message': message,
|
||||
},
|
||||
)
|
||||
|
||||
return json.dumps(output)
|
||||
|
@ -1,3 +1,13 @@
|
||||
"""
|
||||
A RESTful HTTP API for nftables
|
||||
Copyright 2024, Georg Pfuetzenreuter <mail@georg-pfuetzenreuter.net>
|
||||
|
||||
Licensed under the EUPL, Version 1.2 or - as soon they will be approved by the European Commission - subsequent versions of the EUPL (the "Licence").
|
||||
You may not use this work except in compliance with the Licence.
|
||||
An English copy of the Licence is shipped in a file called LICENSE along with this applications source code.
|
||||
You may obtain copies of the Licence in any of the official languages at https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12.
|
||||
"""
|
||||
|
||||
import json
|
||||
|
||||
|
||||
|
50
ruff.toml
Normal file
50
ruff.toml
Normal file
@ -0,0 +1,50 @@
|
||||
[lint]
|
||||
# https://docs.astral.sh/ruff/rules/
|
||||
extend-select = [
|
||||
"A", # flake8-builtins
|
||||
"ARG", # flake8-unused-arguments
|
||||
"BLE", # flake8-blind-except
|
||||
"C4", # flake8-comprehensions
|
||||
"COM", # flake8-commas
|
||||
"CPY001",# flake8-copyright
|
||||
"E", # pycodestyle
|
||||
"E261", # spaces before inline comments
|
||||
"ERA", # eradicate
|
||||
"EXE", # flake8-executable
|
||||
"FBT", # flake8-boolean-trap
|
||||
"I", # isort
|
||||
"INP", # flake8-no-pep420
|
||||
"ISC", # flake8-implicit-str-concat
|
||||
"N", # pep8-naming
|
||||
"PL", # Pylint
|
||||
"RET", # flake8-return
|
||||
"RSE", # flake8-raise
|
||||
"RUF", # Ruff-specific rules
|
||||
"S", # flake8-bandit
|
||||
"SIM", # flake8-simplify
|
||||
"T20", # flake8-print
|
||||
"UP", # pyupgrade
|
||||
"W", # pycodestyle
|
||||
"YTT", # flake8-2020
|
||||
]
|
||||
ignore = [
|
||||
"E501", # line lengths
|
||||
"FBT002", # booleans as function arguments
|
||||
"S603", # https://github.com/astral-sh/ruff/issues/4045
|
||||
"S607", # makes subprocess calls in test suite more portable
|
||||
]
|
||||
preview = true
|
||||
explicit-preview-rules = true
|
||||
|
||||
[lint.per-file-ignores]
|
||||
"tests/*.py" = [
|
||||
"INP001", # tests do not need to be part of a package
|
||||
"S101", # allow "assert" in test suites
|
||||
"T201", # lazy printing is ok in tests
|
||||
]
|
||||
|
||||
[lint.pydocstyle]
|
||||
convention = "pep257"
|
||||
|
||||
[lint.isort]
|
||||
force-wrap-aliases = true
|
@ -1,9 +1,20 @@
|
||||
"""
|
||||
Tests for the RESTful HTTP API for nftables
|
||||
Copyright 2024, Georg Pfuetzenreuter <mail@georg-pfuetzenreuter.net>
|
||||
|
||||
Licensed under the EUPL, Version 1.2 or - as soon they will be approved by the European Commission - subsequent versions of the EUPL (the "Licence").
|
||||
You may not use this work except in compliance with the Licence.
|
||||
An English copy of the Licence is shipped in a file called LICENSE along with this applications source code.
|
||||
You may obtain copies of the Licence in any of the official languages at https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12.
|
||||
"""
|
||||
|
||||
from falcon import testing
|
||||
from pytest import exit, fixture
|
||||
from nftables import Nftables
|
||||
from pytest import exit, fixture
|
||||
|
||||
from nftables_api.app import app
|
||||
|
||||
|
||||
def run_nft(nft, cmd):
|
||||
rc, out, err = nft.cmd(cmd)
|
||||
if rc != 0:
|
||||
|
@ -1,11 +1,22 @@
|
||||
from pytest import mark
|
||||
from falcon import HTTP_CREATED, HTTP_OK
|
||||
"""
|
||||
Tests for the RESTful HTTP API for nftables
|
||||
Copyright 2024, Georg Pfuetzenreuter <mail@georg-pfuetzenreuter.net>
|
||||
|
||||
Licensed under the EUPL, Version 1.2 or - as soon they will be approved by the European Commission - subsequent versions of the EUPL (the "Licence").
|
||||
You may not use this work except in compliance with the Licence.
|
||||
An English copy of the Licence is shipped in a file called LICENSE along with this applications source code.
|
||||
You may obtain copies of the Licence in any of the official languages at https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12.
|
||||
"""
|
||||
|
||||
from json import dumps, loads
|
||||
|
||||
from falcon import HTTP_CREATED, HTTP_OK
|
||||
from pytest import mark
|
||||
|
||||
vs = [4, 6]
|
||||
|
||||
@mark.parametrize('v', vs)
|
||||
def test_get_set(client, nft_ruleset_populated_sets, v):
|
||||
def test_get_set(client, nft_ruleset_populated_sets, v): # noqa ARG001, nft is not needed here
|
||||
want_out = {
|
||||
4: ["192.168.0.0/24", "127.0.0.1"],
|
||||
6: ["fd80::/64", "fe80::1"],
|
||||
@ -22,11 +33,6 @@ def test_append_to_set(client, nft_ruleset_populated_sets, v, plvariant, plforma
|
||||
nft = nft_ruleset_populated_sets
|
||||
|
||||
# all the matrixes could be moved to parameters
|
||||
want_out = {
|
||||
4: ["192.168.0.0/24", "127.0.0.1", "192.168.5.0/26"],
|
||||
6: ["fd80::/64", "fe80::1", "fd10:f00::/128"],
|
||||
}
|
||||
|
||||
if plformat == 'string':
|
||||
if plvariant == 'address':
|
||||
to_add = {
|
||||
|
Loading…
Reference in New Issue
Block a user