refactoring

This commit is contained in:
Devaev Maxim 2020-05-29 06:44:28 +03:00
parent cf5bf8e147
commit d79ed9f1be
2 changed files with 100 additions and 60 deletions

View File

@ -0,0 +1,95 @@
# ========================================================================== #
# #
# KVMD - The main Pi-KVM daemon. #
# #
# Copyright (C) 2018 Maxim Devaev <mdevaev@gmail.com> #
# #
# 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()

View File

@ -52,10 +52,6 @@ from ...plugins.msd import BaseMsd
from ...validators import ValidatorError 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_quality
from ...validators.kvm import valid_stream_fps 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 get_exposed_ws
from .http import make_json_response from .http import make_json_response
from .http import make_json_exception from .http import make_json_exception
from .http import set_request_auth_info
from .http import HttpServer from .http import HttpServer
from .api.auth import AuthApi
from .api.auth import check_request_auth
from .api.log import LogApi from .api.log import LogApi
from .api.wol import WolApi from .api.wol import WolApi
from .api.hid import HidApi 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): class _Events(Enum):
INFO_STATE = "info_state" INFO_STATE = "info_state"
WOL_STATE = "wol_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.__apis: List[object] = [
self, self,
AuthApi(auth_manager),
LogApi(log_reader), LogApi(log_reader),
WolApi(wol), WolApi(wol),
HidApi(hid, keymap_path), 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(), "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 # ===== SYSTEM
@exposed_http("GET", "/info") @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: def __add_app_route(self, app: aiohttp.web.Application, exposed: HttpExposed) -> None:
async def wrapper(request: aiohttp.web.Request) -> aiohttp.web.Response: async def wrapper(request: aiohttp.web.Request) -> aiohttp.web.Response:
try: try:
if exposed.auth_required and self.__auth_manager.is_auth_enabled(): await check_request_auth(self.__auth_manager, exposed, request)
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()
return (await exposed.handler(request)) return (await exposed.handler(request))
except IsBusyError as err: except IsBusyError as err:
return make_json_exception(err, 409) return make_json_exception(err, 409)
except (ValidatorError, OperationError) as err: 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) return make_json_exception(err, 401)
except ForbiddenError as err: except ForbiddenError as err:
return make_json_exception(err, 403) return make_json_exception(err, 403)
app.router.add_route(exposed.method, exposed.path, wrapper) app.router.add_route(exposed.method, exposed.path, wrapper)
async def __on_shutdown(self, _: aiohttp.web.Application) -> None: async def __on_shutdown(self, _: aiohttp.web.Application) -> None: