Skip to content
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

Replace flask-restful with flask-restx #1422

Merged
merged 3 commits into from
Jan 15, 2025
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion nipap/debian/control
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Description: Neat IP Address Planner

Package: nipapd
Architecture: all
Depends: debconf, nipap-common, python3 (>= 3.6), ${misc:Depends}, python3-psycopg2, python3-flask, python3-flask-xml-rpc-re, python3-flask-restful, python3-flask-compress, python3-tornado, python3-parsedatetime, python3-tz, python3-dateutil, python3-psutil, python3-pyparsing, python3-jwt, python3-requests
Depends: debconf, nipap-common, python3 (>= 3.6), ${misc:Depends}, python3-psycopg2, python3-flask, python3-flask-xml-rpc-re, python3-flask-restx, python3-flask-compress, python3-tornado, python3-parsedatetime, python3-tz, python3-dateutil, python3-psutil, python3-pyparsing, python3-jwt, python3-requests
Description: Neat IP Address Planner XML-RPC daemon
The Neat IP Address Planner, NIPAP, is a system built for efficiently managing
large amounts of IP addresses. This is the XML-RPC daemon.
34 changes: 21 additions & 13 deletions nipap/nipap/rest.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,22 @@

import datetime
import logging
import time
import pytz
import json
from functools import wraps
from flask import Flask, request, Response, got_request_exception, jsonify
from flask_restful import Resource, Api, abort
from flask import request, Response, jsonify
from flask_restx import Resource, Api, Namespace, abort

from .backend import Nipap, NipapError, prefix_search_options_spec
import nipap
from .authlib import AuthFactory, AuthError
from .tracing import create_span_rest

logger = logging.getLogger(__name__)

prefix_ns = Namespace(name="prefixes", description="Prefix operations", validate=True)
garberg marked this conversation as resolved.
Show resolved Hide resolved

def setup(app):
api = Api(app, prefix="/rest/v1")
api.add_resource(NipapPrefixRest, "/prefixes")
api.add_namespace(prefix_ns, path="/prefixes")

return app

Expand All @@ -34,7 +34,10 @@ def combine_request_args():
request_nipap_username = request.headers.get('NIPAP-Username')
request_nipap_fullname = request.headers.get('NIPAP-Full-Name')

request_body = request.json
if request.method in ("POST", "PUT"):
request_body = request.json
else:
request_body = None
request_queries = request.args.to_dict()

if request_authoritative_source:
Expand Down Expand Up @@ -112,6 +115,7 @@ def requires_auth(f):
def decorated(self, *args, **kwargs):
"""
"""

# small hack
args = args + (combine_request_args(),)

Expand All @@ -126,7 +130,7 @@ def decorated(self, *args, **kwargs):
self.logger.debug("Malformed request: got %d parameters" % len(args))
abort(401, error={"code": 401, "message": "Malformed request: got %d parameters" % len(args)})

if type(nipap_args) != dict:
if type(nipap_args) is not dict:
self.logger.debug("Function argument is not struct")
abort(401, error={"code": 401, "message": "Function argument is not struct"})

Expand All @@ -153,7 +157,8 @@ def decorated(self, *args, **kwargs):
if auth_header and auth_header.startswith("Bearer"):
bearer_token = auth_header.split(" ")[1]
if not bearer_token:
return authenticate()
logger.info("Authentication failed, missing auth method")
abort(authenticate())

# init AuthFacory()
af = AuthFactory()
Expand All @@ -164,7 +169,6 @@ def decorated(self, *args, **kwargs):

# authenticated?
if not auth.authenticate():
self.logger.debug("Invalid bearer token")
abort(401, error={"code": 401,
"message": "Invalid bearer token"})
else:
Expand All @@ -173,7 +177,6 @@ def decorated(self, *args, **kwargs):

# authenticated?
if not auth.authenticate():
self.logger.debug("Incorrect username or password.")
abort(401, error={"code": 401, "message": "Incorrect username or password"})

# Replace auth options in API call arguments with auth object
Expand Down Expand Up @@ -207,9 +210,12 @@ def get_query_for_field(field, search_value):
}


class NipapPrefixRest(Resource):
@prefix_ns.route("")
class NipapPrefix(Resource):

def __init__(self, *args, **kwargs):
super().__init__(self, *args, **kwargs)

def __init__(self):
self.nip = Nipap()
self.logger = logging.getLogger(self.__class__.__name__)

Expand Down Expand Up @@ -273,6 +279,7 @@ def post(self, args):
self.logger.error(str(err))
abort(500, error={"code": 500, "message": "Internal error"})


@requires_auth
@create_span_rest
def put(self, args):
Expand All @@ -293,6 +300,7 @@ def put(self, args):
self.logger.error(str(err))
abort(500, error={"code": 500, "message": "Internal error"})


@requires_auth
@create_span_rest
def delete(self, args):
Expand Down
8 changes: 4 additions & 4 deletions nipap/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
zipp==3.21.0
importlib_metadata==8.5.0
Flask==2.1.3
Flask-Compress==1.9.0
Flask==3.0.3
Flask-Compress==1.17
flask-xml-rpc-re==0.1.4
Flask-RESTful==0.3.10
flask-restx==1.3.0
requests==2.32.3
IPy==1.01
Jinja2==3.1.4
MarkupSafe==3.0.2
Werkzeug==2.0.3
Werkzeug==3.1.3
backports.ssl-match-hostname==3.7.0.1
certifi==2024.12.14
itsdangerous==2.2.0
Expand Down
2 changes: 2 additions & 0 deletions tests/test_rest.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,8 @@ def _convert_list_of_unicode_to_str(self, list_of_items):

def _add_prefix(self, attr):
request = requests.post(self.server_url, headers=self.headers, json = attr)
self.assertEqual(request.status_code, 200,
msg=f"Result status code {request.status_code} != 200, response: {request.text}")
garberg marked this conversation as resolved.
Show resolved Hide resolved
text = request.text
result = json.loads(text)
result = dict([(str(k), str(v)) for k, v in list(result.items())])
Expand Down
Loading