Add possibility for signed firmware updates
All checks were successful
ci/woodpecker/push/docker Pipeline was successful
ci/woodpecker/cron/docker Pipeline was successful

This commit is contained in:
Oliver Traber 2025-03-23 14:52:44 +00:00
parent b59aeeb5e5
commit f0eff338ff
Signed by: Bluemedia
GPG key ID: C0674B105057136C
5 changed files with 46 additions and 7 deletions

View file

@ -0,0 +1,32 @@
"""Add signature fields to firmware_update table
Revision ID: 506cc8d086c9
Revises: 00edfb13e611
Create Date: 2025-03-23 14:49:42.662564+00:00
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = '506cc8d086c9'
down_revision: Union[str, None] = '00edfb13e611'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('firmware_updates', sa.Column('signing_certificate', sa.Text(), nullable=True))
op.add_column('firmware_updates', sa.Column('signature', sa.String(), nullable=True))
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('firmware_updates', 'signature')
op.drop_column('firmware_updates', 'signing_certificate')
# ### end Alembic commands ###

View file

@ -1,5 +1,5 @@
import uuid import uuid
from sqlalchemy import Column, DateTime, Enum, ForeignKey, Integer, String, Uuid from sqlalchemy import Column, DateTime, Enum, ForeignKey, Integer, String, Text, Uuid
from app.database import Base from app.database import Base
from app.schemas.firmware_update import FirmwareUpdateStatus from app.schemas.firmware_update import FirmwareUpdateStatus
@ -16,5 +16,7 @@ class FirmwareUpdate(Base):
location = Column(String) location = Column(String)
retrieve_date_time = Column(DateTime) retrieve_date_time = Column(DateTime)
install_date_time = Column(DateTime, nullable=True) install_date_time = Column(DateTime, nullable=True)
signing_certificate = Column(Text, nullable=True)
signature = Column(String, nullable=True)
chargepoint_id = Column(Uuid, ForeignKey("chargepoints.id"), index=True) chargepoint_id = Column(Uuid, ForeignKey("chargepoints.id"), index=True)

View file

@ -356,7 +356,7 @@ async def submit_firmware_update(
if chargepoint_manager.is_connected(chargepoint_id) == False: if chargepoint_manager.is_connected(chargepoint_id) == False:
raise HTTPException(status_code=503, detail="Chargepoint not connected.") raise HTTPException(status_code=503, detail="Chargepoint not connected.")
try: try:
firmware_update, status = await firmware_service.submit_firmware_update(firmware_update_id) _, status = await firmware_service.submit_firmware_update(firmware_update_id)
return FirmwareUpdateSubmissionResponse(firmware_update, status) return FirmwareUpdateSubmissionResponse(status=status)
except TimeoutError: except TimeoutError:
raise HTTPException(status_code=503, detail="Chargepoint didn't respond in time.") raise HTTPException(status_code=503, detail="Chargepoint didn't respond in time.")

View file

@ -28,6 +28,8 @@ class FirmwareUpdateBase(BaseModel):
location: str location: str
retrieve_date_time: datetime retrieve_date_time: datetime
install_date_time: Optional[datetime] install_date_time: Optional[datetime]
signing_certificate: Optional[str]
signature: Optional[str]
class FirmwareUpdate(FirmwareUpdateBase): class FirmwareUpdate(FirmwareUpdateBase):
id: UUID id: UUID
@ -38,5 +40,4 @@ class FirmwareUpdateCreate(FirmwareUpdateBase):
pass pass
class FirmwareUpdateSubmissionResponse(BaseModel): class FirmwareUpdateSubmissionResponse(BaseModel):
firmware_update: FirmwareUpdate
status: str status: str

View file

@ -23,7 +23,9 @@ async def create_firmware_update(chargepoint_id: UUID, firmware_update: Firmware
location=firmware_update.location, location=firmware_update.location,
retrieve_date_time=firmware_update.retrieve_date_time, retrieve_date_time=firmware_update.retrieve_date_time,
install_date_time=firmware_update.install_date_time, install_date_time=firmware_update.install_date_time,
chargepoint_id=db_chargepoint.id chargepoint_id=db_chargepoint.id,
signing_certificate=firmware_update.signing_certificate,
signature=firmware_update.signature
) )
db.add(db_firmware_update) db.add(db_firmware_update)
db.commit() db.commit()
@ -43,7 +45,9 @@ async def submit_firmware_update(firmware_update_id: UUID) -> tuple[FirmwareUpda
firmware=FirmwareType( firmware=FirmwareType(
location=db_firmware_update.location, location=db_firmware_update.location,
retrieve_date_time=db_firmware_update.retrieve_date_time.isoformat(), retrieve_date_time=db_firmware_update.retrieve_date_time.isoformat(),
install_date_time=db_firmware_update.install_date_time.isoformat() install_date_time=db_firmware_update.install_date_time.isoformat(),
signing_certificate=db_firmware_update.signing_certificate,
signature=db_firmware_update.signature
) )
)) ))
if result.status == "Accepted" or result.status == "AcceptedCanceled": if result.status == "Accepted" or result.status == "AcceptedCanceled":
@ -58,5 +62,5 @@ async def update_firmware_status(chargepoint_identity: str, request_id: int, sta
with SessionLocal() as db: with SessionLocal() as db:
db_chargepoint = db.query(ChargePoint).filter(ChargePoint.identity == chargepoint_identity).first() db_chargepoint = db.query(ChargePoint).filter(ChargePoint.identity == chargepoint_identity).first()
db_firmware_update = db.query(FirmwareUpdate).filter(FirmwareUpdate.chargepoint_id == db_chargepoint.id).filter(FirmwareUpdate.request_id == request_id).first() db_firmware_update = db.query(FirmwareUpdate).filter(FirmwareUpdate.chargepoint_id == db_chargepoint.id).filter(FirmwareUpdate.request_id == request_id).first()
db_firmware_update.status = status db_firmware_update.status = FirmwareUpdateStatus(status)
db.commit() db.commit()