Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Return all user fields except password #163

Merged
merged 1 commit into from
Jan 25, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
"""initial

Revision ID: ecd6a3f19a8f
Revision ID: a754f05fd8e5
Revises:
Create Date: 2025-01-18 00:10:08.290307
Create Date: 2025-01-25 19:46:28.006950

"""

Expand All @@ -11,7 +11,7 @@


# revision identifiers, used by Alembic.
revision = "ecd6a3f19a8f"
revision = "a754f05fd8e5"
down_revision = None
branch_labels = None
depends_on = None
Expand Down Expand Up @@ -51,7 +51,7 @@ def upgrade():
"users",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("email", sa.Text(), nullable=False),
sa.Column("password", sa.Text(), nullable=False),
sa.Column("password", sa.Text(), nullable=True),
sa.Column("active", sa.Boolean(), nullable=True),
sa.Column("admin", sa.Boolean(), nullable=True),
sa.PrimaryKeyConstraint("id"),
Expand Down
8 changes: 5 additions & 3 deletions freenit/api/auth/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
from freenit.auth import authorize, decode, encode, encrypt
from freenit.config import getConfig
from freenit.mail import sendmail
from freenit.models.safe import UserSafe
from freenit.models.user import User

config = getConfig()
Expand All @@ -24,7 +23,7 @@ class TokenExpire(pydantic.BaseModel):


class LoginResponse(pydantic.BaseModel):
user: UserSafe
user: User
expire: TokenExpire


Expand All @@ -49,6 +48,7 @@ async def login(credentials: LoginInput, response: Response):
httponly=True,
secure=config.auth.secure,
)
user.password = None
return {
"user": user,
"expire": {
Expand Down Expand Up @@ -78,6 +78,7 @@ async def register_sql(credentials: LoginInput) -> User:
active=False,
)
await user.save()
user.password = None
return user


Expand Down Expand Up @@ -105,7 +106,7 @@ async def register(credentials: LoginInput, host=Header(default="")):
return {"status": True}


@api.post("/auth/verify", response_model=UserSafe, tags=["auth"])
@api.post("/auth/verify", response_model=User, tags=["auth"])
async def verify(verification: Verification):
user = await decode(verification.verification)
await user.update(active=True)
Expand All @@ -117,6 +118,7 @@ async def refresh(request: Request, response: Response):
user = await authorize(request, cookie="refresh")
access = encode(user)
response.set_cookie("access", access, httponly=True, secure=config.auth.secure)
user.password = None
return {
"user": user,
"expire": {
Expand Down
44 changes: 27 additions & 17 deletions freenit/api/role/sql.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
from freenit.decorators import description
from freenit.models.pagination import Page, paginate
from freenit.models.role import Role, RoleOptional
from freenit.models.safe import RoleSafe, UserSafe
from freenit.models.user import User
from freenit.permissions import role_perms

Expand All @@ -21,30 +20,35 @@ async def get(
page: int = Header(default=1),
perpage: int = Header(default=10),
_: User = Depends(role_perms),
) -> Page[RoleSafe]:
return await paginate(Role.objects, page, perpage)
) -> Page[Role]:
return await paginate(
Role.objects.select_related("users").exclude_fields("users__password"),
page,
perpage,
)

@staticmethod
async def post(role: Role, _: User = Depends(role_perms)) -> RoleSafe:
async def post(role: Role, _: User = Depends(role_perms)) -> Role:
await role.save()
return role


@route("/roles/{id}", tags=tags)
class RoleDetailAPI:
@staticmethod
async def get(id, _: User = Depends(role_perms)) -> RoleSafe:
async def get(id, _: User = Depends(role_perms)) -> Role:
try:
role = await Role.objects.get(pk=id)
role = (
await Role.objects.select_related("users")
.exclude_fields("users__password")
.get(pk=id)
)
except ormar.exceptions.NoMatch:
raise HTTPException(status_code=404, detail="No such role")
await role.load_all(follow=True)
return role

@staticmethod
async def patch(
id, role_data: RoleOptional, _: User = Depends(role_perms)
) -> RoleSafe:
async def patch(id, role_data: RoleOptional, _: User = Depends(role_perms)) -> Role:
if Role.dbtype() == "sql":
try:
role = await Role.objects.get(pk=id)
Expand All @@ -58,7 +62,7 @@ async def patch(
)

@staticmethod
async def delete(id, _: User = Depends(role_perms)) -> RoleSafe:
async def delete(id, _: User = Depends(role_perms)) -> Role:
try:
role = await Role.objects.get(pk=id)
except ormar.exceptions.NoMatch:
Expand All @@ -71,12 +75,15 @@ async def delete(id, _: User = Depends(role_perms)) -> RoleSafe:
class RoleUserAPI:
@staticmethod
@description("Assign user to role")
async def post(role_id, user_id, _: User = Depends(role_perms)) -> UserSafe:
async def post(role_id, user_id, _: User = Depends(role_perms)) -> User:
try:
user = await User.objects.get(pk=user_id)
user = (
await User.objects.select_related("roles")
.exclude_fields("password")
.get(pk=user_id)
)
except ormar.exceptions.NoMatch:
raise HTTPException(status_code=404, detail="No such user")
await user.load_all()
for role in user.roles:
if role.id == role_id:
raise HTTPException(status_code=409, detail="User already assigned")
Expand All @@ -89,16 +96,19 @@ async def post(role_id, user_id, _: User = Depends(role_perms)) -> UserSafe:

@staticmethod
@description("Deassign user to role")
async def delete(role_id, user_id, _: User = Depends(role_perms)) -> UserSafe:
async def delete(role_id, user_id, _: User = Depends(role_perms)) -> User:
try:
user = await User.objects.get(pk=user_id)
user = (
await User.objects.select_related("roles")
.exclude_fields("password")
.get(pk=user_id)
)
except ormar.exceptions.NoMatch:
raise HTTPException(status_code=404, detail="No such user")
try:
role = await Role.objects.get(pk=role_id)
except ormar.exceptions.NoMatch:
raise HTTPException(status_code=404, detail="No such role")
await user.load_all()
try:
await user.roles.remove(role)
except ormar.exceptions.NoMatch:
Expand Down
26 changes: 16 additions & 10 deletions freenit/api/user/sql.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
from freenit.config import getConfig
from freenit.decorators import description
from freenit.models.pagination import Page, paginate
from freenit.models.safe import UserSafe
from freenit.models.user import User, UserOptional
from freenit.permissions import profile_perms, user_perms

Expand All @@ -24,23 +23,30 @@ async def get(
page: int = Header(default=1),
perpage: int = Header(default=10),
_: User = Depends(user_perms),
) -> Page[UserSafe]:
return await paginate(User.objects, page, perpage)
) -> Page[User]:
return await paginate(
User.objects.select_related("roles").exclude_fields("password"),
page,
perpage,
)


@route("/users/{id}", tags=tags)
class UserDetailAPI:
@staticmethod
async def get(id, _: User = Depends(user_perms)) -> UserSafe:
async def get(id, _: User = Depends(user_perms)) -> User:
try:
user = await User.objects.get(pk=id)
user = (
await User.objects.select_related("roles")
.exclude_fields("password")
.get(pk=id)
)
except ormar.exceptions.NoMatch:
raise HTTPException(status_code=404, detail="No such user")
await user.load_all(follow=True)
return user

@staticmethod
async def patch(id, data: UserOptional, _: User = Depends(user_perms)) -> UserSafe:
async def patch(id, data: UserOptional, _: User = Depends(user_perms)) -> User:
if data.password:
data.password = encrypt(data.password)
try:
Expand All @@ -51,7 +57,7 @@ async def patch(id, data: UserOptional, _: User = Depends(user_perms)) -> UserSa
return user

@staticmethod
async def delete(id, _: User = Depends(user_perms)) -> UserSafe:
async def delete(id, _: User = Depends(user_perms)) -> User:
try:
user = await User.objects.get(pk=id)
except ormar.exceptions.NoMatch:
Expand All @@ -64,15 +70,15 @@ async def delete(id, _: User = Depends(user_perms)) -> UserSafe:
class ProfileDetailAPI:
@staticmethod
@description("Get my profile")
async def get(user: User = Depends(profile_perms)) -> UserSafe:
async def get(user: User = Depends(profile_perms)) -> User:
await user.load_all()
return user

@staticmethod
@description("Edit my profile")
async def patch(
data: UserOptional, user: User = Depends(profile_perms)
) -> UserSafe:
) -> User:
if data.password:
data.password = encrypt(data.password)
await user.patch(data)
Expand Down
3 changes: 1 addition & 2 deletions freenit/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ async def decode(token):
import ormar.exceptions

try:
user = await User.objects.get(pk=pk)
user = await User.objects.select_related("roles").get(pk=pk)
return user
except ormar.exceptions.NoMatch:
raise HTTPException(status_code=403, detail="Unauthorized")
Expand All @@ -46,7 +46,6 @@ async def authorize(request: Request, roles=[], allof=[], cookie="access"):
raise HTTPException(status_code=403, detail="Unauthorized")
user = await decode(token)
if user.dbtype() == "sql":
await user.load_all()
if not user.active:
raise HTTPException(status_code=403, detail="Permission denied")
if user.admin:
Expand Down
12 changes: 0 additions & 12 deletions freenit/models/safe.py

This file was deleted.

2 changes: 1 addition & 1 deletion freenit/models/sql/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ async def patch(self, fields):
class OrmarUserMixin:
id: int = ormar.Integer(primary_key=True)
email: pydantic.EmailStr = ormar.Text(unique=True)
password: str = ormar.Text()
password: str = ormar.Text(nullable=True)
active: bool = ormar.Boolean(default=False)
admin: bool = ormar.Boolean(default=False)

Expand Down
6 changes: 5 additions & 1 deletion freenit/models/sql/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,16 @@

class BaseUser(OrmarBaseModel, OrmarUserMixin):
def check(self, password: str) -> bool:
if self.password is None:
return False
return verify(password, self.password)

@classmethod
async def login(cls, credentials) -> BaseUser:
try:
user = await cls.objects.get(email=credentials.email, active=True)
user = await cls.objects.select_related("roles").get(
email=credentials.email, active=True
)
except ormar.exceptions.NoMatch:
raise HTTPException(status_code=403, detail="Failed to login")
if user.check(credentials.password):
Expand Down
Loading