diff options
author | Devaev Maxim <[email protected]> | 2020-05-29 06:44:28 +0300 |
---|---|---|
committer | Devaev Maxim <[email protected]> | 2020-05-29 06:44:28 +0300 |
commit | d79ed9f1be494d019543bd531bbe09e57946f438 (patch) | |
tree | 9a4731b2878652608e4abd08a48309042c883a54 | |
parent | cf5bf8e14732e7c0bdc67a08a0e410b81b2d4572 (diff) |
refactoring
-rw-r--r-- | kvmd/apps/kvmd/api/auth.py | 95 | ||||
-rw-r--r-- | kvmd/apps/kvmd/server.py | 65 |
2 files changed, 100 insertions, 60 deletions
diff --git a/kvmd/apps/kvmd/api/auth.py b/kvmd/apps/kvmd/api/auth.py new file mode 100644 index 00000000..80996e7e --- /dev/null +++ b/kvmd/apps/kvmd/api/auth.py @@ -0,0 +1,95 @@ +# ========================================================================== # +# # +# KVMD - The main Pi-KVM daemon. # +# # +# Copyright (C) 2018 Maxim Devaev <[email protected]> # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see <https://www.gnu.org/licenses/>. # +# # +# ========================================================================== # + + +from aiohttp.web import Request +from aiohttp.web import Response + +from ....validators.auth import valid_user +from ....validators.auth import valid_passwd +from ....validators.auth import valid_auth_token + +from ..http import UnauthorizedError +from ..http import ForbiddenError +from ..http import HttpExposed +from ..http import exposed_http +from ..http import make_json_response +from ..http import set_request_auth_info + +from ..auth import AuthManager + + +# ===== +_COOKIE_AUTH_TOKEN = "auth_token" + + +async def check_request_auth(auth_manager: AuthManager, exposed: HttpExposed, request: Request) -> None: + if exposed.auth_required and auth_manager.is_auth_enabled(): + user = request.headers.get("X-KVMD-User", "") + passwd = request.headers.get("X-KVMD-Passwd", "") + token = request.cookies.get(_COOKIE_AUTH_TOKEN, "") + + if user: + user = valid_user(user) + set_request_auth_info(request, f"{user} (xhdr)") + if not (await auth_manager.authorize(user, valid_passwd(passwd))): + raise ForbiddenError() + + elif token: + user = auth_manager.check(valid_auth_token(token)) + if not user: + set_request_auth_info(request, "- (token)") + raise ForbiddenError() + set_request_auth_info(request, f"{user} (token)") + + else: + raise UnauthorizedError() + + +class AuthApi: + def __init__(self, auth_manager: AuthManager) -> None: + self.__auth_manager = auth_manager + + # ===== + + @exposed_http("POST", "/auth/login", auth_required=False) + async def __login_handler(self, request: Request) -> Response: + if self.__auth_manager.is_auth_enabled(): + credentials = await request.post() + token = await self.__auth_manager.login( + user=valid_user(credentials.get("user", "")), + passwd=valid_passwd(credentials.get("passwd", "")), + ) + if token: + return make_json_response(set_cookies={_COOKIE_AUTH_TOKEN: token}) + raise ForbiddenError() + return make_json_response() + + @exposed_http("POST", "/auth/logout") + async def __logout_handler(self, request: Request) -> Response: + if self.__auth_manager.is_auth_enabled(): + token = valid_auth_token(request.cookies.get(_COOKIE_AUTH_TOKEN, "")) + self.__auth_manager.logout(token) + return make_json_response() + + @exposed_http("GET", "/auth/check") + async def __check_handler(self, _: Request) -> Response: + return make_json_response() diff --git a/kvmd/apps/kvmd/server.py b/kvmd/apps/kvmd/server.py index 2c63cb0b..9392c826 100644 --- a/kvmd/apps/kvmd/server.py +++ b/kvmd/apps/kvmd/server.py @@ -52,10 +52,6 @@ from ...plugins.msd import BaseMsd from ...validators import ValidatorError -from ...validators.auth import valid_user -from ...validators.auth import valid_passwd -from ...validators.auth import valid_auth_token - from ...validators.kvm import valid_stream_quality from ...validators.kvm import valid_stream_fps @@ -78,9 +74,11 @@ from .http import get_exposed_http from .http import get_exposed_ws from .http import make_json_response from .http import make_json_exception -from .http import set_request_auth_info from .http import HttpServer +from .api.auth import AuthApi +from .api.auth import check_request_auth + from .api.log import LogApi from .api.wol import WolApi from .api.hid import HidApi @@ -89,12 +87,6 @@ from .api.msd import MsdApi # ===== -_HEADER_AUTH_USER = "X-KVMD-User" -_HEADER_AUTH_PASSWD = "X-KVMD-Passwd" - -_COOKIE_AUTH_TOKEN = "auth_token" - - class _Events(Enum): INFO_STATE = "info_state" WOL_STATE = "wol_state" @@ -136,6 +128,7 @@ class KvmdServer(HttpServer): # pylint: disable=too-many-arguments,too-many-ins self.__apis: List[object] = [ self, + AuthApi(auth_manager), LogApi(log_reader), WolApi(wol), HidApi(hid, keymap_path), @@ -166,32 +159,6 @@ class KvmdServer(HttpServer): # pylint: disable=too-many-arguments,too-many-ins "extras": await self.__info_manager.get_extras(), } - # ===== AUTH - - @exposed_http("POST", "/auth/login", auth_required=False) - async def __auth_login_handler(self, request: aiohttp.web.Request) -> aiohttp.web.Response: - if self.__auth_manager.is_auth_enabled(): - credentials = await request.post() - token = await self.__auth_manager.login( - user=valid_user(credentials.get("user", "")), - passwd=valid_passwd(credentials.get("passwd", "")), - ) - if token: - return make_json_response(set_cookies={_COOKIE_AUTH_TOKEN: token}) - raise ForbiddenError() - return make_json_response() - - @exposed_http("POST", "/auth/logout") - async def __auth_logout_handler(self, request: aiohttp.web.Request) -> aiohttp.web.Response: - if self.__auth_manager.is_auth_enabled(): - token = valid_auth_token(request.cookies.get(_COOKIE_AUTH_TOKEN, "")) - self.__auth_manager.logout(token) - return make_json_response() - - @exposed_http("GET", "/auth/check") - async def __auth_check_handler(self, _: aiohttp.web.Request) -> aiohttp.web.Response: - return make_json_response() - # ===== SYSTEM @exposed_http("GET", "/info") @@ -307,29 +274,8 @@ class KvmdServer(HttpServer): # pylint: disable=too-many-arguments,too-many-ins def __add_app_route(self, app: aiohttp.web.Application, exposed: HttpExposed) -> None: async def wrapper(request: aiohttp.web.Request) -> aiohttp.web.Response: try: - if exposed.auth_required and self.__auth_manager.is_auth_enabled(): - user = request.headers.get(_HEADER_AUTH_USER, "") - passwd = request.headers.get(_HEADER_AUTH_PASSWD, "") - token = request.cookies.get(_COOKIE_AUTH_TOKEN, "") - - if user: - user = valid_user(user) - set_request_auth_info(request, f"{user} (xhdr)") - if not (await self.__auth_manager.authorize(user, valid_passwd(passwd))): - raise ForbiddenError() - - elif token: - user = self.__auth_manager.check(valid_auth_token(token)) - if not user: - set_request_auth_info(request, "- (token)") - raise ForbiddenError() - set_request_auth_info(request, f"{user} (token)") - - else: - raise UnauthorizedError() - + await check_request_auth(self.__auth_manager, exposed, request) return (await exposed.handler(request)) - except IsBusyError as err: return make_json_exception(err, 409) except (ValidatorError, OperationError) as err: @@ -338,7 +284,6 @@ class KvmdServer(HttpServer): # pylint: disable=too-many-arguments,too-many-ins return make_json_exception(err, 401) except ForbiddenError as err: return make_json_exception(err, 403) - app.router.add_route(exposed.method, exposed.path, wrapper) async def __on_shutdown(self, _: aiohttp.web.Application) -> None: |