exception-based permissions

This commit is contained in:
Jörn-Michael Miehe 2022-04-07 08:53:54 +00:00
parent 64f8c416ab
commit 48d8eb077d
3 changed files with 74 additions and 43 deletions

View file

@ -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:

View file

@ -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:

View file

@ -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)