rename "capability" -> "tag"
This commit is contained in:
parent
e11f96b0af
commit
bb53bab0c0
6 changed files with 97 additions and 97 deletions
|
@ -4,8 +4,8 @@ Package `db`: ORM and schemas for database content.
|
||||||
|
|
||||||
from .connection import Connection
|
from .connection import Connection
|
||||||
from .device import Device, DeviceBase, DeviceCreate, DeviceRead
|
from .device import Device, DeviceBase, DeviceCreate, DeviceRead
|
||||||
|
from .tag import TagValue
|
||||||
from .user import User, UserBase, UserCreate, UserRead
|
from .user import User, UserBase, UserCreate, UserRead
|
||||||
from .user_capability import UserCapabilityType
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"Connection",
|
"Connection",
|
||||||
|
@ -17,5 +17,5 @@ __all__ = [
|
||||||
"UserBase",
|
"UserBase",
|
||||||
"UserCreate",
|
"UserCreate",
|
||||||
"UserRead",
|
"UserRead",
|
||||||
"UserCapabilityType",
|
"TagValue",
|
||||||
]
|
]
|
||||||
|
|
56
api/kiwi_vpn_api/db/tag.py
Normal file
56
api/kiwi_vpn_api/db/tag.py
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
"""
|
||||||
|
Python representation of `tag` table.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from enum import Enum
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
from sqlmodel import Field, Relationship, SQLModel
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from .user import User
|
||||||
|
|
||||||
|
|
||||||
|
class TagValue(Enum):
|
||||||
|
"""
|
||||||
|
Allowed values for tags
|
||||||
|
"""
|
||||||
|
|
||||||
|
admin = "admin"
|
||||||
|
login = "login"
|
||||||
|
issue = "issue"
|
||||||
|
renew = "renew"
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return self.value
|
||||||
|
|
||||||
|
|
||||||
|
class TagBase(SQLModel):
|
||||||
|
"""
|
||||||
|
Common to all representations of tags
|
||||||
|
"""
|
||||||
|
|
||||||
|
tag_value: str = Field(primary_key=True)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _(self) -> TagValue:
|
||||||
|
"""
|
||||||
|
Transform into a `TagValue`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return TagValue(self.tag_value)
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return self.tag_value
|
||||||
|
|
||||||
|
|
||||||
|
class Tag(TagBase, table=True):
|
||||||
|
"""
|
||||||
|
Representation of `tag` table
|
||||||
|
"""
|
||||||
|
|
||||||
|
user_name: str = Field(primary_key=True, foreign_key="user.name")
|
||||||
|
|
||||||
|
user: "User" = Relationship(
|
||||||
|
back_populates="tags",
|
||||||
|
)
|
|
@ -13,7 +13,7 @@ from sqlmodel import Field, Relationship, SQLModel
|
||||||
from ..config import Config
|
from ..config import Config
|
||||||
from .connection import Connection
|
from .connection import Connection
|
||||||
from .device import Device
|
from .device import Device
|
||||||
from .user_capability import UserCapability, UserCapabilityType
|
from .tag import Tag, TagValue
|
||||||
|
|
||||||
|
|
||||||
class UserBase(SQLModel):
|
class UserBase(SQLModel):
|
||||||
|
@ -77,7 +77,7 @@ class User(UserBase, table=True):
|
||||||
|
|
||||||
password: str
|
password: str
|
||||||
|
|
||||||
capabilities: list[UserCapability] = Relationship(
|
tags: list[Tag] = Relationship(
|
||||||
back_populates="user",
|
back_populates="user",
|
||||||
sa_relationship_kwargs={
|
sa_relationship_kwargs={
|
||||||
"lazy": "joined",
|
"lazy": "joined",
|
||||||
|
@ -164,43 +164,43 @@ class User(UserBase, table=True):
|
||||||
db.delete(self)
|
db.delete(self)
|
||||||
db.commit()
|
db.commit()
|
||||||
|
|
||||||
def get_capabilities(self) -> set[UserCapabilityType]:
|
def get_tags(self) -> set[TagValue]:
|
||||||
"""
|
"""
|
||||||
Return the capabilities of this user.
|
Return the tags of this user.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return set(
|
return set(
|
||||||
capability._
|
tag._
|
||||||
for capability in self.capabilities
|
for tag in self.tags
|
||||||
)
|
)
|
||||||
|
|
||||||
def set_capabilities(
|
def set_tags(
|
||||||
self,
|
self,
|
||||||
capabilities: Sequence[UserCapabilityType],
|
tags: Sequence[TagValue],
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Change the capabilities of this user.
|
Change the tags of this user.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.capabilities = [
|
self.tags = [
|
||||||
UserCapability(
|
Tag(
|
||||||
user_name=self.name,
|
user_name=self.name,
|
||||||
capability_name=capability.value,
|
tag_value=tag.value,
|
||||||
) for capability in capabilities
|
) for tag in tags
|
||||||
]
|
]
|
||||||
|
|
||||||
def _can(
|
def _can(
|
||||||
self,
|
self,
|
||||||
capability: UserCapabilityType,
|
tag: TagValue,
|
||||||
) -> bool:
|
) -> bool:
|
||||||
"""
|
"""
|
||||||
Check if this user has a capability.
|
Check if this user has a tag.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return (
|
return (
|
||||||
capability in self.get_capabilities()
|
tag in self.get_tags()
|
||||||
# admin can do everything
|
# admin can do everything
|
||||||
or UserCapabilityType.admin in self.get_capabilities()
|
or TagValue.admin in self.get_tags()
|
||||||
)
|
)
|
||||||
|
|
||||||
def can_edit(
|
def can_edit(
|
||||||
|
@ -214,7 +214,7 @@ class User(UserBase, table=True):
|
||||||
return (
|
return (
|
||||||
user.name == self.name
|
user.name == self.name
|
||||||
# admin can edit everything
|
# admin can edit everything
|
||||||
or self._can(UserCapabilityType.admin)
|
or self._can(TagValue.admin)
|
||||||
)
|
)
|
||||||
|
|
||||||
def is_admin(
|
def is_admin(
|
||||||
|
@ -224,8 +224,8 @@ class User(UserBase, table=True):
|
||||||
Check if this user is an admin.
|
Check if this user is an admin.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# is admin with "admin" capability
|
# is admin with "admin" tag
|
||||||
return self._can(UserCapabilityType.admin)
|
return self._can(TagValue.admin)
|
||||||
|
|
||||||
def can_login(
|
def can_login(
|
||||||
self,
|
self,
|
||||||
|
@ -235,8 +235,8 @@ class User(UserBase, table=True):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return (
|
return (
|
||||||
# can login with "login" capability
|
# can login with "login" tag
|
||||||
self._can(UserCapabilityType.login)
|
self._can(TagValue.login)
|
||||||
# admins can always login
|
# admins can always login
|
||||||
or self.is_admin()
|
or self.is_admin()
|
||||||
)
|
)
|
||||||
|
@ -253,7 +253,7 @@ class User(UserBase, table=True):
|
||||||
# user can edit itself
|
# user can edit itself
|
||||||
self.name == user.name
|
self.name == user.name
|
||||||
# admin can edit every user
|
# admin can edit every user
|
||||||
or user._can(UserCapabilityType.admin)
|
or user._can(TagValue.admin)
|
||||||
)
|
)
|
||||||
|
|
||||||
def can_be_deleted_by(
|
def can_be_deleted_by(
|
||||||
|
@ -266,7 +266,7 @@ class User(UserBase, table=True):
|
||||||
|
|
||||||
return (
|
return (
|
||||||
# only admin can delete users
|
# only admin can delete users
|
||||||
user._can(UserCapabilityType.admin)
|
user._can(TagValue.admin)
|
||||||
# even admin cannot delete itself
|
# even admin cannot delete itself
|
||||||
and self.name != user.name
|
and self.name != user.name
|
||||||
)
|
)
|
||||||
|
@ -282,5 +282,5 @@ class User(UserBase, table=True):
|
||||||
return (
|
return (
|
||||||
device.owner_name == self.name
|
device.owner_name == self.name
|
||||||
# admin owns everything
|
# admin owns everything
|
||||||
or self._can(UserCapabilityType.admin)
|
or self._can(TagValue.admin)
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,56 +0,0 @@
|
||||||
"""
|
|
||||||
Python representation of `user_capability` table.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from enum import Enum
|
|
||||||
from typing import TYPE_CHECKING
|
|
||||||
|
|
||||||
from sqlmodel import Field, Relationship, SQLModel
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from .user import User
|
|
||||||
|
|
||||||
|
|
||||||
class UserCapabilityType(Enum):
|
|
||||||
"""
|
|
||||||
Allowed values for capabilities
|
|
||||||
"""
|
|
||||||
|
|
||||||
admin = "admin"
|
|
||||||
login = "login"
|
|
||||||
issue = "issue"
|
|
||||||
renew = "renew"
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
|
||||||
return self.value
|
|
||||||
|
|
||||||
|
|
||||||
class UserCapabilityBase(SQLModel):
|
|
||||||
"""
|
|
||||||
Common to all representations of capabilities
|
|
||||||
"""
|
|
||||||
|
|
||||||
capability_name: str = Field(primary_key=True)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def _(self) -> UserCapabilityType:
|
|
||||||
"""
|
|
||||||
Transform into a `Capability`.
|
|
||||||
"""
|
|
||||||
|
|
||||||
return UserCapabilityType(self.capability_name)
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
|
||||||
return self.capability_name
|
|
||||||
|
|
||||||
|
|
||||||
class UserCapability(UserCapabilityBase, table=True):
|
|
||||||
"""
|
|
||||||
Representation of `user_capability` table
|
|
||||||
"""
|
|
||||||
|
|
||||||
user_name: str = Field(primary_key=True, foreign_key="user.name")
|
|
||||||
|
|
||||||
user: "User" = Relationship(
|
|
||||||
back_populates="capabilities",
|
|
||||||
)
|
|
|
@ -7,7 +7,7 @@ from fastapi import APIRouter, Depends, HTTPException, status
|
||||||
from sqlmodel import select
|
from sqlmodel import select
|
||||||
|
|
||||||
from ..config import Config
|
from ..config import Config
|
||||||
from ..db import Connection, User, UserCapabilityType, UserCreate
|
from ..db import Connection, TagValue, User, UserCreate
|
||||||
from ._common import Responses, get_current_user_if_admin
|
from ._common import Responses, get_current_user_if_admin
|
||||||
|
|
||||||
router = APIRouter(prefix="/admin", tags=["admin"])
|
router = APIRouter(prefix="/admin", tags=["admin"])
|
||||||
|
@ -64,7 +64,7 @@ async def create_initial_admin(
|
||||||
|
|
||||||
# create an administrative user
|
# create an administrative user
|
||||||
new_user = User.create(user=admin_user)
|
new_user = User.create(user=admin_user)
|
||||||
new_user.set_capabilities([UserCapabilityType.admin])
|
new_user.set_tags([TagValue.admin])
|
||||||
new_user.update()
|
new_user.update()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ from fastapi.security import OAuth2PasswordRequestForm
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
from ..config import Config
|
from ..config import Config
|
||||||
from ..db import User, UserCapabilityType, UserCreate, UserRead
|
from ..db import TagValue, User, UserCreate, UserRead
|
||||||
from ._common import (Responses, get_current_user_if_admin,
|
from ._common import (Responses, get_current_user_if_admin,
|
||||||
get_current_user_if_exists, get_user_by_name)
|
get_current_user_if_exists, get_user_by_name)
|
||||||
|
|
||||||
|
@ -94,7 +94,7 @@ async def add_user(
|
||||||
if new_user is None:
|
if new_user is None:
|
||||||
raise HTTPException(status_code=status.HTTP_409_CONFLICT)
|
raise HTTPException(status_code=status.HTTP_409_CONFLICT)
|
||||||
|
|
||||||
new_user.set_capabilities([UserCapabilityType.login])
|
new_user.set_tags([TagValue.login])
|
||||||
new_user.update()
|
new_user.update()
|
||||||
|
|
||||||
# return the created user on success
|
# return the created user on success
|
||||||
|
@ -130,7 +130,7 @@ async def remove_user(
|
||||||
|
|
||||||
|
|
||||||
@router.post(
|
@router.post(
|
||||||
"/{user_name}/capabilities",
|
"/{user_name}/tags",
|
||||||
responses={
|
responses={
|
||||||
status.HTTP_200_OK: Responses.OK,
|
status.HTTP_200_OK: Responses.OK,
|
||||||
status.HTTP_400_BAD_REQUEST: Responses.NOT_INSTALLED,
|
status.HTTP_400_BAD_REQUEST: Responses.NOT_INSTALLED,
|
||||||
|
@ -138,22 +138,22 @@ async def remove_user(
|
||||||
status.HTTP_403_FORBIDDEN: Responses.NEEDS_ADMIN,
|
status.HTTP_403_FORBIDDEN: Responses.NEEDS_ADMIN,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
async def extend_capabilities(
|
async def extend_tags(
|
||||||
capabilities: list[UserCapabilityType],
|
tags: list[TagValue],
|
||||||
_: User = Depends(get_current_user_if_admin),
|
_: User = Depends(get_current_user_if_admin),
|
||||||
user: User = Depends(get_user_by_name),
|
user: User = Depends(get_user_by_name),
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
POST ./{user_name}/capabilities: Add capabilities to a user.
|
POST ./{user_name}/tags: Add tags to a user.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
user.set_capabilities(user.get_capabilities() | set(capabilities))
|
user.set_tags(user.get_tags() | set(tags))
|
||||||
|
|
||||||
user.update()
|
user.update()
|
||||||
|
|
||||||
|
|
||||||
@router.delete(
|
@router.delete(
|
||||||
"/{user_name}/capabilities",
|
"/{user_name}/tags",
|
||||||
responses={
|
responses={
|
||||||
status.HTTP_200_OK: Responses.OK,
|
status.HTTP_200_OK: Responses.OK,
|
||||||
status.HTTP_400_BAD_REQUEST: Responses.NOT_INSTALLED,
|
status.HTTP_400_BAD_REQUEST: Responses.NOT_INSTALLED,
|
||||||
|
@ -161,15 +161,15 @@ async def extend_capabilities(
|
||||||
status.HTTP_403_FORBIDDEN: Responses.NEEDS_ADMIN,
|
status.HTTP_403_FORBIDDEN: Responses.NEEDS_ADMIN,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
async def remove_capabilities(
|
async def remove_tags(
|
||||||
capabilities: list[UserCapabilityType],
|
tags: list[TagValue],
|
||||||
_: User = Depends(get_current_user_if_admin),
|
_: User = Depends(get_current_user_if_admin),
|
||||||
user: User = Depends(get_user_by_name),
|
user: User = Depends(get_user_by_name),
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
DELETE ./{user_name}/capabilities: Remove capabilities from a user.
|
DELETE ./{user_name}/tags: Remove tags from a user.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
user.set_capabilities(user.get_capabilities() - set(capabilities))
|
user.set_tags(user.get_tags() - set(tags))
|
||||||
|
|
||||||
user.update()
|
user.update()
|
||||||
|
|
Loading…
Reference in a new issue