exception-based permissions
This commit is contained in:
parent
64f8c416ab
commit
48d8eb077d
3 changed files with 74 additions and 43 deletions
|
|
@ -220,67 +220,71 @@ class User(UserBase, table=True):
|
||||||
for tag in (set(self.__tags) - set(tags))
|
for tag in (set(self.__tags) - set(tags))
|
||||||
]
|
]
|
||||||
|
|
||||||
def can_edit(
|
def check_edit(
|
||||||
self,
|
self,
|
||||||
target: User | Device,
|
target: User | Device,
|
||||||
) -> bool:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Check if this user can edit another user or a device.
|
Check if this user can edit another user or a device.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# admin can "edit" everything
|
# admin can "edit" everything
|
||||||
if self.is_admin:
|
if self.is_admin:
|
||||||
return True
|
return None
|
||||||
|
|
||||||
# user can "edit" itself
|
# user can only "edit" itself
|
||||||
if isinstance(target, User):
|
if isinstance(target, User) and target == self:
|
||||||
return target == self
|
return None
|
||||||
|
|
||||||
# user can edit its owned devices
|
# user can edit its owned devices
|
||||||
return target.owner == self
|
if isinstance(target, Device) and target.owner == self:
|
||||||
|
return None
|
||||||
|
|
||||||
def can_admin(
|
# deny by default
|
||||||
|
raise PermissionError()
|
||||||
|
|
||||||
|
def check_admin(
|
||||||
self,
|
self,
|
||||||
target: User | Device,
|
target: User | Device,
|
||||||
) -> bool:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Check if this user can administer another user or a device.
|
Check if this user can administer another user or a device.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# only admin can "admin" anything
|
# only admin can "admin" anything
|
||||||
if not self.is_admin:
|
if not self.is_admin:
|
||||||
return False
|
raise PermissionError("Must be admin")
|
||||||
|
|
||||||
# admin canot "admin itself"!
|
# admin cannot "admin" itself!
|
||||||
if isinstance(target, User) and target == self:
|
if isinstance(target, User) and target == self:
|
||||||
return False
|
raise PermissionError("Can't administer self")
|
||||||
|
|
||||||
# admin can "admin" everything else
|
# admin can "admin" everything else
|
||||||
return True
|
return None
|
||||||
|
|
||||||
def can_create(
|
def check_create(
|
||||||
self,
|
self,
|
||||||
target: type,
|
target: type,
|
||||||
owner: User | None = None,
|
owner: User | None = None,
|
||||||
) -> bool:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Check if this user can create another user or a device.
|
Check if this user can create another user or a device.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# can never create anything but users or devices
|
# can never create anything but users or devices
|
||||||
if not issubclass(target, (User, Device)):
|
if not issubclass(target, (User, Device)):
|
||||||
return False
|
raise PermissionError(f"Cannot create target type {target}")
|
||||||
|
|
||||||
# admin can "create" everything
|
# admin can "create" everything
|
||||||
if self.is_admin:
|
if self.is_admin:
|
||||||
return True
|
return None
|
||||||
|
|
||||||
# user can only create devices for itself
|
# user can only create devices for itself
|
||||||
if target is Device and owner == self:
|
if target is Device and owner == self:
|
||||||
return True
|
return None
|
||||||
|
|
||||||
# deny be default
|
# deny by default
|
||||||
return False
|
raise PermissionError()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def can_issue(self) -> bool:
|
def can_issue(self) -> bool:
|
||||||
|
|
|
||||||
|
|
@ -39,9 +39,12 @@ async def add_device(
|
||||||
- 409: device creation unsuccessful
|
- 409: device creation unsuccessful
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# check permission
|
# check permissions
|
||||||
if not current_user.can_create(Device, owner):
|
try:
|
||||||
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN)
|
current_user.check_create(Device, owner)
|
||||||
|
|
||||||
|
except PermissionError as e:
|
||||||
|
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN) from e
|
||||||
|
|
||||||
# create the new device
|
# create the new device
|
||||||
new_device = Device.create(
|
new_device = Device.create(
|
||||||
|
|
@ -80,9 +83,12 @@ async def remove_device(
|
||||||
- 403: no user permission to edit device
|
- 403: no user permission to edit device
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# check permission
|
# check permissions
|
||||||
if not current_user.can_edit(device):
|
try:
|
||||||
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN)
|
current_user.check_edit(device)
|
||||||
|
|
||||||
|
except PermissionError as e:
|
||||||
|
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN) from e
|
||||||
|
|
||||||
# delete device
|
# delete device
|
||||||
device.delete()
|
device.delete()
|
||||||
|
|
@ -113,9 +119,12 @@ async def request_certificate_issuance(
|
||||||
- 409: device certificate cannot be "issued"
|
- 409: device certificate cannot be "issued"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# check permission
|
# check permissions
|
||||||
if not current_user.can_edit(device):
|
try:
|
||||||
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN)
|
current_user.check_edit(device)
|
||||||
|
|
||||||
|
except PermissionError as e:
|
||||||
|
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN) from e
|
||||||
|
|
||||||
# can only "request" on an uncertified device
|
# can only "request" on an uncertified device
|
||||||
if device.status is not DeviceStatus.uncertified:
|
if device.status is not DeviceStatus.uncertified:
|
||||||
|
|
@ -161,9 +170,12 @@ async def request_certificate_renewal(
|
||||||
- 409: device certificate cannot be "renewed"
|
- 409: device certificate cannot be "renewed"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# check permission
|
# check permissions
|
||||||
if not current_user.can_edit(device):
|
try:
|
||||||
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN)
|
current_user.check_edit(device)
|
||||||
|
|
||||||
|
except PermissionError as e:
|
||||||
|
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN) from e
|
||||||
|
|
||||||
# can only "renew" on an already certified device
|
# can only "renew" on an already certified device
|
||||||
if device.status is not DeviceStatus.certified:
|
if device.status is not DeviceStatus.certified:
|
||||||
|
|
@ -209,9 +221,12 @@ async def revoke_certificate(
|
||||||
- 409: device certificate cannot be "revoked"
|
- 409: device certificate cannot be "revoked"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# check permission
|
# check permissions
|
||||||
if not current_user.can_edit(device):
|
try:
|
||||||
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN)
|
current_user.check_edit(device)
|
||||||
|
|
||||||
|
except PermissionError as e:
|
||||||
|
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN) from e
|
||||||
|
|
||||||
# can only "revoke" on a currently certified device
|
# can only "revoke" on a currently certified device
|
||||||
if device.status is not DeviceStatus.certified:
|
if device.status is not DeviceStatus.certified:
|
||||||
|
|
|
||||||
|
|
@ -107,8 +107,11 @@ async def add_user(
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# check permissions
|
# check permissions
|
||||||
if not current_user.can_create(User):
|
try:
|
||||||
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN)
|
current_user.check_create(User)
|
||||||
|
|
||||||
|
except PermissionError as e:
|
||||||
|
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN) from e
|
||||||
|
|
||||||
# create the new user
|
# create the new user
|
||||||
new_user = User.create(user=user)
|
new_user = User.create(user=user)
|
||||||
|
|
@ -147,8 +150,11 @@ async def remove_user(
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# check permissions
|
# check permissions
|
||||||
if not current_user.can_admin(user):
|
try:
|
||||||
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN)
|
current_user.check_admin(user)
|
||||||
|
|
||||||
|
except PermissionError as e:
|
||||||
|
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN) from e
|
||||||
|
|
||||||
# delete user
|
# delete user
|
||||||
user.delete()
|
user.delete()
|
||||||
|
|
@ -178,8 +184,11 @@ async def extend_tags(
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# check permissions
|
# check permissions
|
||||||
if not current_user.can_admin(user):
|
try:
|
||||||
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN)
|
current_user.check_admin(user)
|
||||||
|
|
||||||
|
except PermissionError as e:
|
||||||
|
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN) from e
|
||||||
|
|
||||||
# change user
|
# change user
|
||||||
user.add_tags(tags)
|
user.add_tags(tags)
|
||||||
|
|
@ -209,8 +218,11 @@ async def remove_tags(
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# check permissions
|
# check permissions
|
||||||
if not current_user.can_admin(user):
|
try:
|
||||||
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN)
|
current_user.check_admin(user)
|
||||||
|
|
||||||
|
except PermissionError as e:
|
||||||
|
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN) from e
|
||||||
|
|
||||||
# change user
|
# change user
|
||||||
user.remove_tags(tags)
|
user.remove_tags(tags)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue