mirror of
https://github.com/arcan1s/ahriman.git
synced 2025-07-15 06:55:48 +00:00
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:
@ -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]
|
||||
"""
|
||||
|
@ -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:
|
||||
|
@ -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))
|
||||
|
@ -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:
|
||||
|
@ -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))
|
||||
|
@ -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:
|
||||
|
@ -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))
|
||||
|
||||
|
@ -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"]
|
||||
|
@ -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:
|
||||
|
@ -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:
|
||||
|
@ -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))
|
||||
|
@ -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
|
||||
|
||||
|
Reference in New Issue
Block a user