mirror of
https://github.com/RYGhub/royalnet.git
synced 2024-11-27 13:34:28 +00:00
API login routes completed
This commit is contained in:
parent
741b9f50db
commit
bb3aa1fd85
13 changed files with 89 additions and 30 deletions
|
@ -1,12 +1,14 @@
|
||||||
# Imports go here!
|
# Imports go here!
|
||||||
from .api_royalnet_version import ApiRoyalnetVersionStar
|
from .api_royalnet_version import ApiRoyalnetVersionStar
|
||||||
from .api_royalnet_login import ApiRoyalnetLoginStar
|
from .api_login_royalnet import ApiLoginRoyalnetStar
|
||||||
|
from .api_token_info import ApiTokenInfoStar
|
||||||
|
|
||||||
|
|
||||||
# Enter the PageStars of your Pack here!
|
# Enter the PageStars of your Pack here!
|
||||||
available_page_stars = [
|
available_page_stars = [
|
||||||
ApiRoyalnetVersionStar,
|
ApiRoyalnetVersionStar,
|
||||||
ApiRoyalnetLoginStar,
|
ApiLoginRoyalnetStar,
|
||||||
|
ApiTokenInfoStar,
|
||||||
]
|
]
|
||||||
|
|
||||||
# Don't change this, it should automatically generate __all__
|
# Don't change this, it should automatically generate __all__
|
||||||
|
|
|
@ -6,10 +6,10 @@ from ..tables.aliases import Alias
|
||||||
from ..tables.tokens import Token
|
from ..tables.tokens import Token
|
||||||
|
|
||||||
|
|
||||||
class ApiRoyalnetLoginStar(ApiStar):
|
class ApiLoginRoyalnetStar(ApiStar):
|
||||||
path = "/api/royalnet/login/v1"
|
path = "/api/login/royalnet/v1"
|
||||||
|
|
||||||
async def api(self, data: ApiDataDict) -> dict:
|
async def api(self, data: ApiData) -> dict:
|
||||||
TokenT = self.alchemy.get(Token)
|
TokenT = self.alchemy.get(Token)
|
||||||
UserT = self.alchemy.get(User)
|
UserT = self.alchemy.get(User)
|
||||||
AliasT = self.alchemy.get(Alias)
|
AliasT = self.alchemy.get(Alias)
|
||||||
|
@ -20,7 +20,7 @@ class ApiRoyalnetLoginStar(ApiStar):
|
||||||
async with self.session_acm() as session:
|
async with self.session_acm() as session:
|
||||||
user: User = await ru.asyncify(session.query(UserT).filter_by(username=username).one_or_none)
|
user: User = await ru.asyncify(session.query(UserT).filter_by(username=username).one_or_none)
|
||||||
if user is None:
|
if user is None:
|
||||||
raise NotFoundException("User not found")
|
raise NotFoundError("User not found")
|
||||||
pswd_check = user.test_password(password)
|
pswd_check = user.test_password(password)
|
||||||
if not pswd_check:
|
if not pswd_check:
|
||||||
raise ApiError("Invalid password")
|
raise ApiError("Invalid password")
|
|
@ -5,7 +5,7 @@ from royalnet.constellation.api import *
|
||||||
class ApiRoyalnetVersionStar(ApiStar):
|
class ApiRoyalnetVersionStar(ApiStar):
|
||||||
path = "/api/royalnet/version/v1"
|
path = "/api/royalnet/version/v1"
|
||||||
|
|
||||||
async def api(self, data: ApiDataDict) -> dict:
|
async def api(self, data: ApiData) -> dict:
|
||||||
return {
|
return {
|
||||||
"semantic": rv.semantic
|
"semantic": rv.semantic
|
||||||
}
|
}
|
||||||
|
|
13
royalnet/backpack/stars/api_token_info.py
Normal file
13
royalnet/backpack/stars/api_token_info.py
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import datetime
|
||||||
|
import royalnet.utils as ru
|
||||||
|
from royalnet.constellation.api import *
|
||||||
|
from ..tables.users import User
|
||||||
|
from ..tables.aliases import Alias
|
||||||
|
|
||||||
|
|
||||||
|
class ApiTokenInfoStar(ApiStar):
|
||||||
|
path = "/api/token/info/v1"
|
||||||
|
|
||||||
|
async def api(self, data: ApiData) -> dict:
|
||||||
|
token = await data.token()
|
||||||
|
return token.json()
|
|
@ -4,6 +4,7 @@ from sqlalchemy import Column, \
|
||||||
ForeignKey
|
ForeignKey
|
||||||
from sqlalchemy.orm import relationship
|
from sqlalchemy.orm import relationship
|
||||||
from sqlalchemy.ext.declarative import declared_attr
|
from sqlalchemy.ext.declarative import declared_attr
|
||||||
|
import royalnet.utils as ru
|
||||||
|
|
||||||
|
|
||||||
class Alias:
|
class Alias:
|
||||||
|
@ -22,10 +23,10 @@ class Alias:
|
||||||
return relationship("User", backref="aliases")
|
return relationship("User", backref="aliases")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def find_user(cls, alchemy, session, alias: str):
|
async def find_user(cls, alchemy, session, alias: str):
|
||||||
result = session.query(alchemy.get(cls)).filter_by(alias=alias.lower()).one_or_none()
|
result = await ru.asyncify(session.query(alchemy.get(cls)).filter_by(alias=alias.lower()).one_or_none)
|
||||||
if result is not None:
|
if result is not None:
|
||||||
result = result.royal
|
result = result.user
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def __init__(self, user: str, alias: str):
|
def __init__(self, user: str, alias: str):
|
||||||
|
|
|
@ -3,6 +3,8 @@ import secrets
|
||||||
from sqlalchemy import *
|
from sqlalchemy import *
|
||||||
from sqlalchemy.orm import *
|
from sqlalchemy.orm import *
|
||||||
from sqlalchemy.ext.declarative import declared_attr
|
from sqlalchemy.ext.declarative import declared_attr
|
||||||
|
import royalnet.utils as ru
|
||||||
|
from .users import User
|
||||||
|
|
||||||
|
|
||||||
class Token:
|
class Token:
|
||||||
|
@ -40,3 +42,7 @@ class Token:
|
||||||
"token": self.token,
|
"token": self.token,
|
||||||
"expiration": self.expiration.isoformat()
|
"expiration": self.expiration.isoformat()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def authenticate(cls, alchemy, session, token: str) -> "Token":
|
||||||
|
return await ru.asyncify(session.query(alchemy.get(cls)).filter_by(token=token).one_or_none)
|
||||||
|
|
|
@ -75,7 +75,6 @@ class CommandData:
|
||||||
Alias.find_user(self._interface.alchemy, self.session, alias)
|
Alias.find_user(self._interface.alchemy, self.session, alias)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@contextlib.asynccontextmanager
|
@contextlib.asynccontextmanager
|
||||||
async def keyboard(self, text, keys: List["KeyboardKey"]):
|
async def keyboard(self, text, keys: List["KeyboardKey"]):
|
||||||
yield
|
yield
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from .apistar import ApiStar
|
from .apistar import ApiStar
|
||||||
from .jsonapi import api_response, api_success, api_error
|
from .jsonapi import api_response, api_success, api_error
|
||||||
from .apidatadict import ApiDataDict
|
from .apidata import ApiData
|
||||||
from .apierrors import ApiError, MissingParameterException, NotFoundException, UnauthorizedException
|
from .apierrors import ApiError, MissingParameterError, NotFoundError, ForbiddenError
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
|
@ -9,8 +9,8 @@ __all__ = [
|
||||||
"api_response",
|
"api_response",
|
||||||
"api_success",
|
"api_success",
|
||||||
"api_error",
|
"api_error",
|
||||||
"ApiDataDict",
|
"ApiData",
|
||||||
"ApiError",
|
"ApiError",
|
||||||
"MissingParameterException",
|
"MissingParameterError",
|
||||||
"NotFoundException",
|
"NotFoundError",
|
||||||
]
|
]
|
||||||
|
|
31
royalnet/constellation/api/apidata.py
Normal file
31
royalnet/constellation/api/apidata.py
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
from .apierrors import MissingParameterError
|
||||||
|
from royalnet.backpack.tables.tokens import Token
|
||||||
|
from royalnet.backpack.tables.users import User
|
||||||
|
from .apierrors import *
|
||||||
|
|
||||||
|
|
||||||
|
class ApiData(dict):
|
||||||
|
def __init__(self, data, star):
|
||||||
|
super().__init__(data)
|
||||||
|
self.star = star
|
||||||
|
self._session = None
|
||||||
|
|
||||||
|
def __missing__(self, key):
|
||||||
|
raise MissingParameterError(f"Missing '{key}'")
|
||||||
|
|
||||||
|
async def token(self) -> Token:
|
||||||
|
token = await Token.authenticate(self.star.alchemy, self.session, self["token"])
|
||||||
|
if token is None:
|
||||||
|
raise ForbiddenError("'token' is invalid")
|
||||||
|
return token
|
||||||
|
|
||||||
|
async def user(self) -> Token:
|
||||||
|
return (await self.token()).user
|
||||||
|
|
||||||
|
@property
|
||||||
|
def session(self):
|
||||||
|
if self._session is None:
|
||||||
|
if self.star.alchemy is None:
|
||||||
|
raise UnsupportedError("'alchemy' is not enabled on this Royalnet instance")
|
||||||
|
self._session = self.star.alchemy.Session()
|
||||||
|
return self._session
|
|
@ -1,6 +0,0 @@
|
||||||
from .apierrors import MissingParameterException
|
|
||||||
|
|
||||||
|
|
||||||
class ApiDataDict(dict):
|
|
||||||
def __missing__(self, key):
|
|
||||||
raise MissingParameterException(f"Missing '{key}'")
|
|
|
@ -2,13 +2,21 @@ class ApiError(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class NotFoundException(ApiError):
|
class NotFoundError(ApiError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class UnauthorizedException(ApiError):
|
class ForbiddenError(ApiError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class MissingParameterException(ApiError):
|
class MissingParameterError(ApiError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class NotImplementedError(ApiError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class UnsupportedError(NotImplementedError):
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -5,8 +5,8 @@ from starlette.requests import Request
|
||||||
from starlette.responses import JSONResponse
|
from starlette.responses import JSONResponse
|
||||||
from ..pagestar import PageStar
|
from ..pagestar import PageStar
|
||||||
from .jsonapi import api_error, api_success
|
from .jsonapi import api_error, api_success
|
||||||
from .apidatadict import ApiDataDict
|
from .apidata import ApiData
|
||||||
from .apierrors import ApiError, NotFoundException
|
from .apierrors import *
|
||||||
|
|
||||||
|
|
||||||
class ApiStar(PageStar, ABC):
|
class ApiStar(PageStar, ABC):
|
||||||
|
@ -19,9 +19,13 @@ class ApiStar(PageStar, ABC):
|
||||||
except JSONDecodeError:
|
except JSONDecodeError:
|
||||||
data = {}
|
data = {}
|
||||||
try:
|
try:
|
||||||
response = await self.api(ApiDataDict(data))
|
response = await self.api(ApiData(data, self))
|
||||||
except NotFoundException as e:
|
except NotFoundError as e:
|
||||||
return api_error(e, code=404)
|
return api_error(e, code=404)
|
||||||
|
except ForbiddenError as e:
|
||||||
|
return api_error(e, code=403)
|
||||||
|
except NotImplementedError as e:
|
||||||
|
return api_error(e, code=501)
|
||||||
except ApiError as e:
|
except ApiError as e:
|
||||||
return api_error(e, code=400)
|
return api_error(e, code=400)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
|
@ -27,6 +27,7 @@ def api_error(error: Exception, code: int = 500) -> JSONResponse:
|
||||||
result = {
|
result = {
|
||||||
"success": False,
|
"success": False,
|
||||||
"error_type": error.__class__.__qualname__,
|
"error_type": error.__class__.__qualname__,
|
||||||
"error_args": list(error.args)
|
"error_args": list(error.args),
|
||||||
|
"error_code": code,
|
||||||
}
|
}
|
||||||
return api_response(result, code=code)
|
return api_response(result, code=code)
|
||||||
|
|
Loading…
Reference in a new issue