diff --git a/server/corvus/api/authentication_api.py b/server/corvus/api/authentication_api.py index ec565c1..960b88d 100644 --- a/server/corvus/api/authentication_api.py +++ b/server/corvus/api/authentication_api.py @@ -21,7 +21,8 @@ AUTH_BLUEPRINT = Blueprint( @AUTH_BLUEPRINT.route('/login', methods=['POST']) @return_json -@authentication_middleware.require(required_auth=Auth.BASIC, required_role=Role.USER) +@authentication_middleware.require( + required_auth=Auth.BASIC, required_role=Role.USER) def login() -> APIResponse: """ Get a token for continued authentication. @@ -34,7 +35,8 @@ def login() -> APIResponse: @AUTH_BLUEPRINT.route('/bump', methods=['POST']) @return_json -@authentication_middleware.require(required_auth=Auth.BASIC, required_role=Role.USER) +@authentication_middleware.require( + required_auth=Auth.BASIC, required_role=Role.USER) def login_bump() -> APIResponse: """ Update the user last seen timestamp. @@ -47,7 +49,8 @@ def login_bump() -> APIResponse: @AUTH_BLUEPRINT.route('/logout', methods=['POST']) @return_json -@authentication_middleware.require(required_auth=Auth.BASIC, required_role=Role.USER) +@authentication_middleware.require( + required_auth=Auth.BASIC, required_role=Role.USER) def logout() -> APIResponse: """ Logout and delete a token. @@ -60,10 +63,11 @@ def logout() -> APIResponse: @AUTH_BLUEPRINT.route('/token', methods=['GET']) @return_json -@authentication_middleware.require(required_auth=Auth.BASIC, required_role=Role.USER) +@authentication_middleware.require( + required_auth=Auth.BASIC, required_role=Role.USER) def get_tokens() -> APIResponse: """ - Get a list of all tokens for the current user + Get a list of all tokens for the current user. :return: a paginated list of user tokens """ @@ -76,10 +80,12 @@ def get_tokens() -> APIResponse: @AUTH_BLUEPRINT.route('/token', methods=['POST']) @return_json -@authentication_middleware.require(required_auth=Auth.BASIC, required_role=Role.USER) +@authentication_middleware.require( + required_auth=Auth.BASIC, required_role=Role.USER) def create_token(): """ - Create a new token with optional parameters + Create a new token with optional parameters. + note: String enabled: Boolean expirationTime: DateTime @@ -89,16 +95,18 @@ def create_token(): requested_token: UserToken = transformation_service.deserialize_model( UserToken, request.json, options=['note', 'enabled', 'expirationTime']) user_token = user_token_service.create( - g.user, requested_token.note, requested_token.enabled, requested_token.expiration_time) + g.user, requested_token.note, + requested_token.enabled, requested_token.expiration_time) return APIResponse(user_token, 200) @AUTH_BLUEPRINT.route('/token/', methods=['GET']) @return_json -@authentication_middleware.require(required_auth=Auth.BASIC, required_role=Role.USER) +@authentication_middleware.require( + required_auth=Auth.BASIC, required_role=Role.USER) def get_token(token: str): """ - Retrieve a specific token for this user + Retrieve a specific token for this user. :param token: The token to retrieve for this user :return: The token if it exists @@ -111,10 +119,11 @@ def get_token(token: str): @AUTH_BLUEPRINT.route('/token/', methods=['DELETE']) @return_json -@authentication_middleware.require(required_auth=Auth.BASIC, required_role=Role.USER) +@authentication_middleware.require( + required_auth=Auth.BASIC, required_role=Role.USER) def delete_token(token: str): """ - Delete a specific token for this user + Delete a specific token for this user. :param token: The token to delete for this user :return: Nothing on success diff --git a/server/corvus/api/health_api.py b/server/corvus/api/health_api.py index 2c97f20..8d6a600 100644 --- a/server/corvus/api/health_api.py +++ b/server/corvus/api/health_api.py @@ -1,4 +1,4 @@ -"""Endpoint to expose the health of the application""" +"""Endpoint to expose the health of the application.""" from flask import Blueprint from corvus.api.decorators import return_json @@ -11,6 +11,11 @@ HEALTH_BLUEPRINT = Blueprint( @HEALTH_BLUEPRINT.route('', methods=['GET']) @return_json def get_health() -> APIResponse: + """ + Retrieve the health for the service. + + :return: + """ return APIResponse( APIMessage(True, 'Service is healthy'), 200 diff --git a/server/corvus/api/user_api.py b/server/corvus/api/user_api.py index e883426..3fce2a5 100644 --- a/server/corvus/api/user_api.py +++ b/server/corvus/api/user_api.py @@ -22,7 +22,8 @@ USER_BLUEPRINT = Blueprint( @USER_BLUEPRINT.route('', methods=['GET']) @return_json -@authentication_middleware.require(required_auth=Auth.TOKEN, required_role=Role.USER) +@authentication_middleware.require( + required_auth=Auth.TOKEN, required_role=Role.USER) def get_users() -> APIResponse: """ Get a list of users. @@ -38,7 +39,8 @@ def get_users() -> APIResponse: @USER_BLUEPRINT.route('/', methods=['GET']) @return_json -@authentication_middleware.require(required_auth=Auth.TOKEN, required_role=Role.USER) +@authentication_middleware.require( + required_auth=Auth.TOKEN, required_role=Role.USER) def get_user(name: str) -> APIResponse: """ Get a user. @@ -53,7 +55,8 @@ def get_user(name: str) -> APIResponse: @USER_BLUEPRINT.route('/', methods=['PATCH']) @return_json -@authentication_middleware.require(required_auth=Auth.TOKEN, required_role=Role.USER) +@authentication_middleware.require( + required_auth=Auth.TOKEN, required_role=Role.USER) def patch_user(name: str) -> APIResponse: """ Patch a user. @@ -72,7 +75,8 @@ def patch_user(name: str) -> APIResponse: @USER_BLUEPRINT.route('', methods=['POST']) @return_json -@authentication_middleware.require(required_auth=Auth.TOKEN, required_role=Role.ADMIN) +@authentication_middleware.require( + required_auth=Auth.TOKEN, required_role=Role.ADMIN) def register_user() -> APIResponse: """ Register a user with the service. @@ -96,7 +100,8 @@ def register_user() -> APIResponse: @USER_BLUEPRINT.route('/', methods=['DELETE']) @return_json -@authentication_middleware.require(required_auth=Auth.TOKEN, required_role=Role.ADMIN) +@authentication_middleware.require( + required_auth=Auth.TOKEN, required_role=Role.ADMIN) def delete_user(name: str) -> APIResponse: """ Delete a user with the service. diff --git a/server/corvus/errors.py b/server/corvus/errors.py index c39e501..6649584 100644 --- a/server/corvus/errors.py +++ b/server/corvus/errors.py @@ -43,8 +43,6 @@ class ClientError(BaseError): class ValidationError(ClientError): """Corvus Validation Error.""" - pass - @return_json def handle_corvus_404_error(exception: HTTPException) -> APIResponse: diff --git a/server/corvus/middleware/authentication_middleware.py b/server/corvus/middleware/authentication_middleware.py index 7419823..2528458 100644 --- a/server/corvus/middleware/authentication_middleware.py +++ b/server/corvus/middleware/authentication_middleware.py @@ -103,7 +103,6 @@ def parse_token_header( return None return Authorization('token', {'username': bytes_to_wsgi(username), 'password': bytes_to_wsgi(token)}) - return None def require_basic_auth(func: Callable) -> Callable: @@ -185,11 +184,11 @@ def require(required_auth: Auth, required_role: Role) -> Callable: def require_decorator(func: Callable) -> Callable: @wraps(func) def decorate(*args: list, **kwargs: dict) -> Any: - f = require_role(required_role)(func) + decorated = require_role(required_role)(func) if required_auth == Auth.BASIC: - f = require_basic_auth(f) + decorated = require_basic_auth(decorated) elif required_auth == Auth.TOKEN: - f = require_token_auth(f) + decorated = require_token_auth(decorated) else: Response( response=APIMessage( @@ -197,6 +196,6 @@ def require(required_auth: Auth, required_role: Role) -> Callable: success=False ), status=500) - return f(*args, **kwargs) + return decorated(*args, **kwargs) return decorate return require_decorator diff --git a/server/corvus/service/role_service.py b/server/corvus/service/role_service.py index c225049..89ffd66 100644 --- a/server/corvus/service/role_service.py +++ b/server/corvus/service/role_service.py @@ -56,7 +56,7 @@ class RoleTree(defaultdict): def find_role(self, request_role: Role) -> List['RoleTree']: """Identify all instances of a role.""" try: - return [role_tree for role_tree in self.roles[request_role]] + return self.roles[request_role] except KeyError: return [] diff --git a/server/corvus/service/user_token_service.py b/server/corvus/service/user_token_service.py index 17aaa45..653cce0 100644 --- a/server/corvus/service/user_token_service.py +++ b/server/corvus/service/user_token_service.py @@ -1,7 +1,7 @@ """Service to handle user_token operations.""" import uuid from datetime import datetime -from typing import Optional, Dict, Callable, Any, List +from typing import Optional, Dict, Callable, Any from flask_sqlalchemy import Pagination from iso8601 import iso8601, ParseError @@ -85,7 +85,8 @@ class UserTokenTransformer(BaseTransformer): try: model.expiration_time = iso8601.parse_date(expiration_time) except ParseError: - raise errors.ValidationError('Cannot parse datetime from %s' % expiration_time) + raise errors.ValidationError( + 'Cannot parse datetime from %s' % expiration_time) def serialize_creation_time(self) -> datetime: """User token creation time.""" @@ -98,7 +99,8 @@ class UserTokenTransformer(BaseTransformer): try: model.creation_time = iso8601.parse_date(creation_time) except ParseError: - raise errors.ValidationError('Cannot parse datetime from %s' % creation_time) + raise errors.ValidationError( + 'Cannot parse datetime from %s' % creation_time) def serialize_last_usage_time(self) -> datetime: """User token last usage time.""" @@ -111,6 +113,7 @@ class UserTokenTransformer(BaseTransformer): model.last_usage_time = iso8601.parse_date(last_usage_time) def serialize_is_valid(self): + """User token is_valid computed value.""" return is_valid_token(self.model) def serialize_version(self) -> int: @@ -211,7 +214,8 @@ def find_by_user_and_token(user: User, token: str) -> Optional[UserToken]: return UserToken.query.filter_by(user_id=user.id, token=token).first() -def find_by_user(user: User, page: int, per_page: int = 20, max_per_page: int = 100) -> Pagination: +def find_by_user(user: User, page: int, per_page: int = 20, + max_per_page: int = 100) -> Pagination: """ Find all tokens for a user @@ -221,4 +225,5 @@ def find_by_user(user: User, page: int, per_page: int = 20, max_per_page: int = :param max_per_page: :return: """ - return UserToken.query.filter_by(user_id=user.id).paginate(page, per_page, True, max_per_page) + return UserToken.query.filter_by( + user_id=user.id).paginate(page, per_page, True, max_per_page) diff --git a/server/mypy.ini b/server/mypy.ini index c440ed6..c73b7b2 100644 --- a/server/mypy.ini +++ b/server/mypy.ini @@ -8,4 +8,3 @@ disallow_subclassing_any = False warn_redundant_casts = True warn_unused_ignores = True strict_optional = True -strict_boolean = False \ No newline at end of file diff --git a/server/run_tests.bat b/server/run_tests.bat new file mode 100644 index 0000000..8fd1ae8 --- /dev/null +++ b/server/run_tests.bat @@ -0,0 +1,30 @@ +SET PIPENV_VERBOSITY=-1 +SET PYTHONPATH=%cd% + +pipenv run python3 --version +pipenv run python3 -m pip --version + +pipenv run pylint --version +pipenv run mypy --version +pipenv run coverage --version +pipenv run pytest --version +pipenv run pycodestyle --version +pipenv run pydocstyle --version + +pipenv run pylint corvus +if %errorlevel% neq 0 exit /b %errorlevel% + +pipenv run mypy corvus tests +if %errorlevel% neq 0 exit /b %errorlevel% + +pipenv run coverage run --source corvus -m pytest +if %errorlevel% neq 0 exit /b %errorlevel% + +pipenv run coverage report --fail-under=85 -m --skip-covered +if %errorlevel% neq 0 exit /b %errorlevel% + +pipenv run pycodestyle corvus tests +if %errorlevel% neq 0 exit /b %errorlevel% + +pipenv run pydocstyle corvus +if %errorlevel% neq 0 exit /b %errorlevel% diff --git a/server/run_tests.sh b/server/run_tests.sh index dd03aa7..6d96259 100755 --- a/server/run_tests.sh +++ b/server/run_tests.sh @@ -3,20 +3,22 @@ set -e set -x +# shellcheck disable=SC2034 +PIPENV_VERBOSITY=-1 -python3 --version -python3 -m pip --version +pipenv run python3 --version +pipenv run python3 -m pip --version -pylint --version -mypy --version -coverage --version -pytest --version -pycodestyle --version -pydocstyle --version +pipenv run pylint --version +pipenv run mypy --version +pipenv run coverage --version +pipenv run pytest --version +pipenv run pycodestyle --version +pipenv run pydocstyle --version -pylint corvus -mypy corvus tests -PYTHONPATH=$(pwd) coverage run --source corvus -m pytest -coverage report --fail-under=85 -m --skip-covered -pycodestyle corvus tests -pydocstyle corvus +pipenv run pylint corvus +pipenv run mypy corvus tests +PYTHONPATH=$(pwd) pipenv run coverage run --source corvus -m pytest +pipenv run coverage report --fail-under=85 -m --skip-covered +pipenv run pycodestyle corvus tests +pipenv run pydocstyle corvus