check issue permission

This commit is contained in:
Jörn-Michael Miehe 2022-04-02 21:24:44 +00:00
parent 2d39c4aaa3
commit d8bdb46a5c
4 changed files with 51 additions and 15 deletions

View file

@ -23,7 +23,6 @@ class DeviceBase(SQLModel):
name: str name: str
type: str type: str
expiry: datetime | None
class DeviceCreate(DeviceBase): class DeviceCreate(DeviceBase):
@ -37,11 +36,13 @@ class DeviceRead(DeviceBase):
Representation of a device read via the API Representation of a device read via the API
""" """
approved: bool id: int | None = Field(primary_key=True)
owner_name: str | None approved: bool | None = Field(default=None)
expiry: datetime | None = Field(default=None)
owner_name: str = Field(foreign_key="user.name")
class Device(DeviceBase, table=True): class Device(DeviceRead, table=True):
""" """
Representation of `device` table Representation of `device` table
""" """
@ -51,10 +52,6 @@ class Device(DeviceBase, table=True):
"name", "name",
),) ),)
id: int | None = Field(primary_key=True)
approved: bool = Field(default=False)
owner_name: str | None = Field(foreign_key="user.name")
# no idea, but "User" (in quotes) doesn't work here # no idea, but "User" (in quotes) doesn't work here
# might be a future problem? # might be a future problem?
owner: User = Relationship( owner: User = Relationship(
@ -74,8 +71,7 @@ class Device(DeviceBase, table=True):
try: try:
with Connection.session as db: with Connection.session as db:
new_device = cls.from_orm(device) new_device = cls.from_orm(device, {"owner_name": owner.name})
new_device.owner = owner
db.add(new_device) db.add(new_device)
db.commit() db.commit()

View file

@ -281,3 +281,23 @@ class User(UserBase, table=True):
# deny be default # deny be default
return False return False
def can_issue(self, device: Device) -> bool:
"""
Check if this user can issue a certificate without approval.
"""
return (
device.approved in (None, False)
and (self.is_admin or self.has_tag(TagValue.issue))
)
def can_renew(self, device: Device) -> bool:
"""
Check if this user can renew a certificate without approval.
"""
return (
device.approved is True
and (self.is_admin or self.has_tag(TagValue.renew))
)

View file

@ -2,6 +2,8 @@
/device endpoints. /device endpoints.
""" """
from datetime import datetime
from fastapi import APIRouter, Depends, HTTPException, status from fastapi import APIRouter, Depends, HTTPException, status
from ..db import Connection, Device, DeviceCreate, DeviceRead, User from ..db import Connection, Device, DeviceCreate, DeviceRead, User
@ -106,7 +108,20 @@ async def request_certificate(
db.add(device) db.add(device)
dn = DistinguishedName.build(device) dn = DistinguishedName.build(device)
easy_rsa.issue( if current_user.can_issue(device):
dn=dn, device.approved = True
cert_type=CertificateType.server,
) if (cert := easy_rsa.issue(
dn=dn,
cert_type=CertificateType.server,
)) is not None:
assert (expiry := cert.get_notAfter()) is not None
date_format, encoding = "%Y%m%d%H%M%SZ", "ascii"
expiry = datetime.strptime(
expiry.decode(encoding),
date_format,
)
device.expiry = expiry
db.commit()

View file

@ -26,6 +26,11 @@
- approved: bool - approved: bool
- expiry - expiry
## Device status
- created (approved = NULL): device has been newly created
- requested (approved = false): certificate has been requested (issue or renew)
- issued (approved = true): certificate has been granted (may be expired)
## Permissions ## Permissions
- admin cannot "admin" itself (to prevent self decapitation) - admin cannot "admin" itself (to prevent self decapitation)
- admin can "edit", "admin" and "create" everything else - admin can "edit", "admin" and "create" everything else
@ -37,5 +42,5 @@
- admin: add or remove tag, delete, generate password - admin: add or remove tag, delete, generate password
### Device ### Device
- edit: change type, delete - edit: change type, delete, request
- admin: approve - admin: approve