add better member name validation, add debug user create endpoint
This commit is contained in:
parent
1bd07dd771
commit
afadffbaac
3 changed files with 66 additions and 14 deletions
|
@ -8,9 +8,7 @@ from foxnouns.db import Member
|
||||||
from foxnouns.db.aio import async_session
|
from foxnouns.db.aio import async_session
|
||||||
from foxnouns.db.util import user_from_ref, is_self
|
from foxnouns.db.util import user_from_ref, is_self
|
||||||
from foxnouns.exceptions import NotFoundError, ErrorCode
|
from foxnouns.exceptions import NotFoundError, ErrorCode
|
||||||
from foxnouns.models import BasePatchModel
|
from foxnouns.models.member import FullMemberModel, MemberPatchModel
|
||||||
from foxnouns.models.member import FullMemberModel
|
|
||||||
from foxnouns.models.fields import ProfileField, FieldEntry, PronounEntry
|
|
||||||
from foxnouns.settings import BASE_DOMAIN
|
from foxnouns.settings import BASE_DOMAIN
|
||||||
|
|
||||||
bp = Blueprint("members_v2", __name__)
|
bp = Blueprint("members_v2", __name__)
|
||||||
|
@ -27,20 +25,19 @@ async def get_members(user_ref: str):
|
||||||
return [FullMemberModel.model_validate(m) for m in user.members]
|
return [FullMemberModel.model_validate(m) for m in user.members]
|
||||||
|
|
||||||
|
|
||||||
class MemberPostData(BasePatchModel):
|
class MemberCreateModel(MemberPatchModel):
|
||||||
name: str = Field(min_length=1, max_length=100) # TODO: validate member names more
|
name: str = Field(
|
||||||
bio: str | None = Field(max_length=1024, default=None)
|
min_length=1,
|
||||||
|
max_length=100,
|
||||||
names: list[FieldEntry] = Field(default=[])
|
pattern=r"^[^@\?!#\/\\\[\]\"\{\}'$%&()+<=>^|~`,\*]{1,100}$",
|
||||||
pronouns: list[PronounEntry] = Field(default=[])
|
)
|
||||||
fields: list[ProfileField] = Field(default=[])
|
|
||||||
|
|
||||||
|
|
||||||
@bp.post("/api/v2/members", host=BASE_DOMAIN)
|
@bp.post("/api/v2/members", host=BASE_DOMAIN)
|
||||||
@require_auth(scope="member.create")
|
@require_auth(scope="member.create")
|
||||||
@validate_request(MemberPostData)
|
@validate_request(MemberCreateModel)
|
||||||
@validate_response(FullMemberModel, 200)
|
@validate_response(FullMemberModel, 200)
|
||||||
async def create_member(data: MemberPostData):
|
async def create_member(data: MemberCreateModel):
|
||||||
async with async_session() as session:
|
async with async_session() as session:
|
||||||
member = Member(
|
member = Member(
|
||||||
user_id=g.user.id,
|
user_id=g.user.id,
|
||||||
|
|
|
@ -6,7 +6,8 @@ from sqlalchemy import select
|
||||||
from foxnouns.auth import require_auth
|
from foxnouns.auth import require_auth
|
||||||
from foxnouns.db import User
|
from foxnouns.db import User
|
||||||
from foxnouns.db.aio import async_session
|
from foxnouns.db.aio import async_session
|
||||||
from foxnouns.db.util import user_from_ref, is_self
|
from foxnouns.db.snowflake import Snowflake
|
||||||
|
from foxnouns.db.util import user_from_ref, is_self, create_token, generate_token
|
||||||
from foxnouns.exceptions import NotFoundError, ErrorCode
|
from foxnouns.exceptions import NotFoundError, ErrorCode
|
||||||
from foxnouns.models import BasePatchModel
|
from foxnouns.models import BasePatchModel
|
||||||
from foxnouns.models.user import UserModel, SelfUserModel, check_username
|
from foxnouns.models.user import UserModel, SelfUserModel, check_username
|
||||||
|
@ -48,6 +49,8 @@ class EditUserRequest(BasePatchModel):
|
||||||
@validate_request(EditUserRequest)
|
@validate_request(EditUserRequest)
|
||||||
@validate_response(SelfUserModel, 200)
|
@validate_response(SelfUserModel, 200)
|
||||||
async def edit_user(data: EditUserRequest):
|
async def edit_user(data: EditUserRequest):
|
||||||
|
"""Updates the current user."""
|
||||||
|
|
||||||
async with async_session() as session:
|
async with async_session() as session:
|
||||||
user = await session.scalar(select(User).where(User.id == g.user.id))
|
user = await session.scalar(select(User).where(User.id == g.user.id))
|
||||||
|
|
||||||
|
@ -61,3 +64,32 @@ async def edit_user(data: EditUserRequest):
|
||||||
await session.commit()
|
await session.commit()
|
||||||
|
|
||||||
return SelfUserModel.model_validate(user)
|
return SelfUserModel.model_validate(user)
|
||||||
|
|
||||||
|
|
||||||
|
class DebugUserData(BasePatchModel):
|
||||||
|
username: str
|
||||||
|
|
||||||
|
|
||||||
|
class DebugUserResponse(SelfUserModel):
|
||||||
|
token: str
|
||||||
|
|
||||||
|
|
||||||
|
@bp.post("/api/v2/users/debug", host=BASE_DOMAIN)
|
||||||
|
@validate_request(DebugUserData)
|
||||||
|
@validate_response(DebugUserResponse, 200)
|
||||||
|
async def debug_create_user(data: DebugUserData):
|
||||||
|
"""Creates a user from just a username, and returns it along with a token.
|
||||||
|
FIXME: this must be removed **BEFORE** deploying to production (or even public testing)
|
||||||
|
"""
|
||||||
|
|
||||||
|
async with async_session() as session:
|
||||||
|
user = User(id=Snowflake.generate_int(), username=data.username)
|
||||||
|
await session.commit()
|
||||||
|
|
||||||
|
session.add(user)
|
||||||
|
token = await create_token(session, user, ["*"])
|
||||||
|
await session.commit()
|
||||||
|
await user.awaitable_attrs.members
|
||||||
|
|
||||||
|
user.token = generate_token(token)
|
||||||
|
return DebugUserResponse.model_validate(user)
|
||||||
|
|
|
@ -1,7 +1,30 @@
|
||||||
from pydantic import Field
|
from pydantic import Field, field_validator
|
||||||
|
|
||||||
|
from . import BasePatchModel
|
||||||
|
from .fields import FieldEntry, ProfileField, PronounEntry
|
||||||
from .user import BaseMemberModel, BaseUserModel
|
from .user import BaseMemberModel, BaseUserModel
|
||||||
|
|
||||||
|
|
||||||
class FullMemberModel(BaseMemberModel):
|
class FullMemberModel(BaseMemberModel):
|
||||||
user: BaseUserModel
|
user: BaseUserModel
|
||||||
|
|
||||||
|
|
||||||
|
class MemberPatchModel(BasePatchModel):
|
||||||
|
name: str | None = Field(
|
||||||
|
min_length=1,
|
||||||
|
max_length=100,
|
||||||
|
default=None,
|
||||||
|
pattern=r"^[^@\?!#\/\\\[\]\"\{\}'$%&()+<=>^|~`,\*]{1,100}$",
|
||||||
|
)
|
||||||
|
bio: str | None = Field(max_length=1024, default=None)
|
||||||
|
|
||||||
|
names: list[FieldEntry] = Field(default=[])
|
||||||
|
pronouns: list[PronounEntry] = Field(default=[])
|
||||||
|
fields: list[ProfileField] = Field(default=[])
|
||||||
|
|
||||||
|
@field_validator("name")
|
||||||
|
@classmethod
|
||||||
|
def check_name(cls, value):
|
||||||
|
if value in [".", "..", "edit"]:
|
||||||
|
raise ValueError("Name is not allowed")
|
||||||
|
return value
|
||||||
|
|
Loading…
Reference in a new issue