From d5bfaa7391f37ebdb1050913157cf0516d2c1a07 Mon Sep 17 00:00:00 2001
From: hanfi <ccc@spahan.ch>
Date: Fri, 16 Jun 2023 08:23:17 +0200
Subject: [PATCH] allow signed updates

---
 backend/main.py    | 14 ++++++++++++--
 backend/models.py  |  3 +--
 backend/schemas.py | 11 +++++++++--
 backend/utils.py   | 23 +++++++++++++++++++++++
 requirements.txt   |  1 +
 5 files changed, 46 insertions(+), 6 deletions(-)

diff --git a/backend/main.py b/backend/main.py
index e889389..3b9af59 100644
--- a/backend/main.py
+++ b/backend/main.py
@@ -37,12 +37,22 @@ def add_item(item: schemas.ItemCreatePrepareShipping, db: Session = Depends(get_
     return utils.prepare_item_shipping(db, item)
 
 
+@app.post("/item/update/{item_uuid}", response_model=schemas.Item)
+def update_item(
+    item_uuid: str, data: schemas.ItemUpdate, db: Session = Depends(get_db)
+):
+    item = utils.get_item_by_uuid(db, UUID(item_uuid))
+    if not item:
+        raise HTTPException(status_code=404, detail="Item not found")
+    return utils.update_item(db, item, data)
+
+
 @app.get("/item/{item_uuid}", response_model=schemas.Item)
 def get_item(item_uuid: str, db: Session = Depends(get_db)):
     item = utils.get_item_by_uuid(db, UUID(item_uuid))
     if not item:
         raise HTTPException(status_code=404, detail="Item not found")
-    return utils.get_item_by_uuid(db, UUID(item_uuid))
+    return item
 
 
 @app.get("/tag/{tag}", response_model=schemas.Item)
@@ -50,7 +60,7 @@ def get_item_by_tag(tag: str, db: Session = Depends(get_db)):
     item = utils.get_item_by_tag(db, tag)
     if not item:
         raise HTTPException(status_code=404, detail="Item not found")
-    return utils.get_item_by_tag(db, tag)
+    return item
 
 
 @app.get("/storages", response_model=list[schemas.Storage])
diff --git a/backend/models.py b/backend/models.py
index fafdc65..84c1cdb 100644
--- a/backend/models.py
+++ b/backend/models.py
@@ -4,7 +4,6 @@ from sqlalchemy import Column as sql_Column
 from sqlalchemy import DateTime as sql_DateTime
 from sqlalchemy import ForeignKey as sql_ForeignKey
 from sqlalchemy import Integer as sql_Integer
-from sqlalchemy import LargeBinary as sql_LargeBinary
 from sqlalchemy import String as sql_String
 from sqlalchemy import Uuid as sql_Uuid
 from sqlalchemy.orm import relationship as sql_relationship
@@ -28,7 +27,7 @@ class Item(db_Base):
     deployed = sql_Column(sql_String(64), nullable=True, default=None)
     deployed_at = sql_Column(sql_DateTime(timezone=True), nullable=True, default=None)
 
-    verification = sql_Column(sql_LargeBinary(57), nullable=True, default=None)
+    verification = sql_Column(sql_String(114), nullable=True, default=None)
     tag = sql_Column(sql_String(6), nullable=True, default=None)
 
     storage = sql_Column(
diff --git a/backend/schemas.py b/backend/schemas.py
index 5ecec4a..27f025e 100644
--- a/backend/schemas.py
+++ b/backend/schemas.py
@@ -12,7 +12,7 @@ class Image(BaseModel):
 
 
 class ItemCreatePrepareShipping(BaseModel):
-    verification: bytes
+    verification: str
     addressee: Union[str, None] = None
     team: Union[str, None] = None
 
@@ -28,6 +28,13 @@ class ItemCheckin(BaseModel):
     amount: Union[int, None] = 1
 
 
+class ItemUpdate(BaseModel):
+    addressee: Union[str, None] = None
+    team: Union[str, None] = None
+    amount: Union[int, None] = None
+    signature: str
+
+
 class Item(BaseModel):
     uuid: UUID4
     amount: int
@@ -41,7 +48,7 @@ class Item(BaseModel):
     deployed: Union[str, None] = None
     deployed_at: Union[datetime, None] = None
 
-    verification: Union[bytes, None] = None
+    verification: Union[str, None] = None
     tag: Union[str, None] = None
 
     storage: Union[str, None] = None
diff --git a/backend/utils.py b/backend/utils.py
index 35e435f..26068dd 100644
--- a/backend/utils.py
+++ b/backend/utils.py
@@ -1,6 +1,8 @@
 from datetime import datetime
 from secrets import token_hex
 
+from cryptography.exceptions import InvalidSignature
+from cryptography.hazmat.primitives.asymmetric.ed448 import Ed448PublicKey
 from pydantic import UUID4
 from sqlalchemy.orm import Session
 
@@ -40,6 +42,27 @@ def prepare_item_shipping(db: Session, item: schemas.ItemCreatePrepareShipping):
     return new_item
 
 
+def update_item(db: Session, item: schemas.Item, data: schemas.ItemUpdate):
+    public_key = Ed448PublicKey.from_public_bytes(bytes.fromhex(item.verification))
+    verify = ""
+    if data.addressee:
+        verify += data.addressee
+        item.addressee = data.addressee
+    if data.team:
+        verify += data.team
+        item.team = data.team
+    if data.amount:
+        verify += str(data.amount)
+        item.amount = data.amount
+    try:
+        public_key.verify(bytes.fromhex(data.signature), bytes(verify, "utf-8"))
+    except InvalidSignature:
+        return None
+    db.commit()
+    db.refresh(item)
+    return item
+
+
 def receive_item_with_image(db: Session, item: schemas.ItemCreateByImageAtStorage):
     new_item = models.Item(storage_uuid=item.storage_uuid)
     db.add(new_item)
diff --git a/requirements.txt b/requirements.txt
index 0b123ed..eab79be 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,3 +1,4 @@
+cryptography==41.0.1
 fastapi==0.95.1
 Jinja2==3.1.2
 python-multipart==0.0.6
-- 
GitLab