feat: forbid form data in html

It has been a while since all pages have moved to json instead of form
data, except for login page. This commit changes login to json data
instead of form one
This commit is contained in:
2023-11-16 16:42:27 +02:00
parent de7184fc3a
commit 18d17d4d52
17 changed files with 72 additions and 133 deletions

View File

@ -20,7 +20,7 @@
from aiohttp_cors import CorsViewMixin # type: ignore[import-untyped]
from aiohttp.web import HTTPBadRequest, HTTPNotFound, Request, StreamResponse, View
from collections.abc import Awaitable, Callable
from typing import Any, TypeVar
from typing import TypeVar
from ahriman.core.auth import Auth
from ahriman.core.configuration import Configuration
@ -138,47 +138,6 @@ class BaseView(View, CorsViewMixin):
raise KeyError(f"Key {key} is missing or empty") from None
return value
async def data_as_json(self, list_keys: list[str]) -> dict[str, Any]:
"""
extract form data and convert it to json object
Args:
list_keys(list[str]): list of keys which must be forced to list from form data
Returns:
dict[str, Any]: form data converted to json. In case if a key is found multiple times
it will be returned as list
"""
raw = await self.request.post()
json: dict[str, Any] = {}
for key, value in raw.items():
if key in json and isinstance(json[key], list):
json[key].append(value)
elif key in json:
json[key] = [json[key], value]
elif key in list_keys:
json[key] = [value]
else:
json[key] = value
return json
async def extract_data(self, list_keys: list[str] | None = None) -> dict[str, Any]:
"""
extract json data from either json or form data
Args:
list_keys(list[str] | None, optional): optional list of keys which must be forced to list from form data
(Default value = None)
Returns:
dict[str, Any]: raw json object or form data converted to json
"""
try:
json: dict[str, Any] = await self.request.json()
return json
except ValueError:
return await self.data_as_json(list_keys or [])
# pylint: disable=not-callable,protected-access
async def head(self) -> StreamResponse: # type: ignore[return]
"""

View File

@ -66,7 +66,7 @@ class AddView(BaseView):
HTTPBadRequest: if bad data is supplied
"""
try:
data = await self.extract_data(["packages", "patches"])
data = await self.request.json()
packages = self.get_non_empty(lambda key: [package for package in data[key] if package], "packages")
patches = [PkgbuildPatch(patch["key"], patch.get("value", "")) for patch in data.get("patches", [])]
except Exception as ex:

View File

@ -104,9 +104,8 @@ class PGPView(BaseView):
Raises:
HTTPBadRequest: if bad data is supplied
"""
data = await self.extract_data()
try:
data = await self.request.json()
key = self.get_non_empty(data.get, "key")
except Exception as ex:
raise HTTPBadRequest(reason=str(ex))

View File

@ -65,7 +65,7 @@ class RebuildView(BaseView):
HTTPBadRequest: if bad data is supplied
"""
try:
data = await self.extract_data(["packages"])
data = await self.request.json()
packages = self.get_non_empty(lambda key: [package for package in data[key] if package], "packages")
depends_on = next(iter(packages))
except Exception as ex:

View File

@ -65,7 +65,7 @@ class RemoveView(BaseView):
HTTPBadRequest: if bad data is supplied
"""
try:
data = await self.extract_data(["packages"])
data = await self.request.json()
packages = self.get_non_empty(lambda key: [package for package in data[key] if package], "packages")
except Exception as ex:
raise HTTPBadRequest(reason=str(ex))

View File

@ -66,7 +66,7 @@ class RequestView(BaseView):
HTTPBadRequest: if bad data is supplied
"""
try:
data = await self.extract_data(["packages", "patches"])
data = await self.request.json()
packages = self.get_non_empty(lambda key: [package for package in data[key] if package], "packages")
patches = [PkgbuildPatch(patch["key"], patch.get("value", "")) for patch in data.get("patches", [])]
except Exception as ex:

View File

@ -65,7 +65,7 @@ class UpdateView(BaseView):
HTTPBadRequest: if bad data is supplied
"""
try:
data = await self.extract_data()
data = await self.request.json()
except Exception as ex:
raise HTTPBadRequest(reason=str(ex))

View File

@ -139,9 +139,9 @@ class LogsView(BaseView):
HTTPNoContent: in case of success response
"""
package_base = self.request.match_info["package"]
data = await self.extract_data()
try:
data = await self.request.json()
created = data["created"]
record = data["message"]
version = data["version"]

View File

@ -142,9 +142,9 @@ class PackageView(BaseView):
HTTPNoContent: in case of success response
"""
package_base = self.request.match_info["package"]
data = await self.extract_data()
try:
data = await self.request.json()
package = Package.from_json(data["package"]) if "package" in data else None
status = BuildStatusEnum(data["status"])
except Exception as ex:

View File

@ -95,9 +95,9 @@ class PatchesView(BaseView):
HTTPNoContent: on success response
"""
package_base = self.request.match_info["package"]
data = await self.extract_data()
try:
data = await self.request.json()
key = data["key"]
value = data["value"]
except Exception as ex:

View File

@ -102,7 +102,7 @@ class StatusView(BaseView):
HTTPNoContent: in case of success response
"""
try:
data = await self.extract_data()
data = await self.request.json()
status = BuildStatusEnum(data["status"])
except Exception as ex:
raise HTTPBadRequest(reason=str(ex))

View File

@ -19,7 +19,7 @@
#
import aiohttp_apispec # type: ignore[import-untyped]
from aiohttp.web import HTTPFound, HTTPMethodNotAllowed, HTTPUnauthorized
from aiohttp.web import HTTPBadRequest, HTTPFound, HTTPMethodNotAllowed, HTTPUnauthorized
from ahriman.core.auth.helpers import remember
from ahriman.models.user_access import UserAccess
@ -93,6 +93,7 @@ class LoginView(BaseView):
description="Login by using username and password",
responses={
302: {"description": "Success response"},
400: {"description": "Bad data is supplied", "schema": ErrorSchema},
401: {"description": "Authorization required", "schema": ErrorSchema},
500: {"description": "Internal server error", "schema": ErrorSchema},
},
@ -107,11 +108,15 @@ class LoginView(BaseView):
HTTPFound: on success response
HTTPUnauthorized: if case of authorization error
"""
data = await self.extract_data()
identity = data.get("username")
try:
data = await self.request.json()
identity = data["username"]
password = data["password"]
except Exception as ex:
raise HTTPBadRequest(reason=str(ex))
response = HTTPFound("/")
if identity is not None and await self.validator.check_credentials(identity, data.get("password")):
if await self.validator.check_credentials(identity, password):
await remember(self.request, response, identity)
raise response