@@ -43,6 +43,7 @@ func (s *Service) ConfigureFirmwareStore(store firmwareObjectStore, ttl time.Dur
|
||||
|
||||
type FirmwareRelease struct {
|
||||
ID string `json:"id"`
|
||||
HouseholdID string `json:"household_id,omitempty"`
|
||||
Model string `json:"model"`
|
||||
HardwareRevision string `json:"hardware_revision,omitempty"`
|
||||
Version string `json:"version"`
|
||||
@@ -53,6 +54,10 @@ type FirmwareRelease struct {
|
||||
SigningKeyID string `json:"signing_key_id"`
|
||||
MinimumSecurityVersion string `json:"minimum_security_version,omitempty"`
|
||||
ReleaseNotes string `json:"release_notes,omitempty"`
|
||||
ReleaseChannel string `json:"release_channel"`
|
||||
Mandatory bool `json:"mandatory"`
|
||||
PublishedAt *time.Time `json:"published_at,omitempty"`
|
||||
CreatedSource string `json:"created_source"`
|
||||
Status string `json:"status"`
|
||||
VerifiedAt *time.Time `json:"verified_at,omitempty"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
@@ -64,7 +69,7 @@ type FirmwareRelease struct {
|
||||
type FirmwareRollout struct {
|
||||
ID string `json:"id"`
|
||||
ReleaseID string `json:"release_id"`
|
||||
HouseholdID string `json:"household_id"`
|
||||
HouseholdID string `json:"household_id,omitempty"`
|
||||
Name string `json:"name"`
|
||||
Status string `json:"status"`
|
||||
TargetPercentage int `json:"target_percentage"`
|
||||
@@ -172,6 +177,8 @@ type FirmwareImpactDevice struct {
|
||||
type PrepareFirmwareInput struct {
|
||||
Model, HardwareRevision, Version, ExpectedDigest, Signature, SigningKeyID, MinimumSecurityVersion, ReleaseNotes string
|
||||
ExpectedSize int64
|
||||
ReleaseChannel string
|
||||
Mandatory bool
|
||||
}
|
||||
|
||||
func (s *Service) RegisterFirmwareSigningKey(ctx context.Context, p identity.Principal, householdPublicID, requestID, keyID, publicKeyBase64 string) error {
|
||||
@@ -312,7 +319,7 @@ func (s *Service) PrepareFirmwareRelease(ctx context.Context, p identity.Princip
|
||||
}
|
||||
tenantHash := sha256.Sum256([]byte(a.tenantID))
|
||||
objectRef := fmt.Sprintf("tenants/%s/firmware/%s/%s/%s.bin", hex.EncodeToString(tenantHash[:16]), safePath(in.Model), safePath(in.HardwareRevision), safePath(in.Version))
|
||||
err = tx.QueryRow(ctx, `INSERT INTO firmware_release(tenant_id,household_id,model,hardware_revision,version,object_ref,expected_size,expected_digest,signature,signing_key_id,minimum_security_version,release_notes,created_by) VALUES($1::uuid,$2::uuid,$3,NULLIF($4,''),$5,$6,$7,$8,$9,$10,NULLIF($11,''),NULLIF($12,''),$13::uuid) RETURNING public_id::text,model,COALESCE(hardware_revision,''),version,expected_size,expected_digest,signature_algorithm,signature,signing_key_id,COALESCE(minimum_security_version,''),COALESCE(release_notes,''),status,verified_at,created_at,updated_at,object_ref`, a.tenantID, a.householdID, in.Model, in.HardwareRevision, in.Version, objectRef, in.ExpectedSize, in.ExpectedDigest, in.Signature, in.SigningKeyID, in.MinimumSecurityVersion, in.ReleaseNotes, a.userID).Scan(firmwareReleaseTargets(&item)...)
|
||||
err = tx.QueryRow(ctx, `INSERT INTO firmware_release(tenant_id,household_id,model,hardware_revision,version,object_ref,expected_size,expected_digest,signature,signing_key_id,minimum_security_version,release_notes,release_channel,mandatory,created_source,created_by) VALUES($1::uuid,$2::uuid,$3,NULLIF($4,''),$5,$6,$7,$8,$9,$10,NULLIF($11,''),NULLIF($12,''),$13,$14,'CUSTOMER',$15::uuid) RETURNING public_id::text,COALESCE(household_id::text,''),model,COALESCE(hardware_revision,''),version,expected_size,expected_digest,signature_algorithm,signature,signing_key_id,COALESCE(minimum_security_version,''),COALESCE(release_notes,''),release_channel,mandatory,published_at,created_source,status,verified_at,created_at,updated_at,object_ref`, a.tenantID, a.householdID, in.Model, in.HardwareRevision, in.Version, objectRef, in.ExpectedSize, in.ExpectedDigest, in.Signature, in.SigningKeyID, in.MinimumSecurityVersion, in.ReleaseNotes, normalizeReleaseChannel(in.ReleaseChannel), in.Mandatory, a.userID).Scan(firmwareReleaseTargets(&item)...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -336,7 +343,15 @@ func safePath(v string) string {
|
||||
return r.Replace(v)
|
||||
}
|
||||
func firmwareReleaseTargets(i *FirmwareRelease) []any {
|
||||
return []any{&i.ID, &i.Model, &i.HardwareRevision, &i.Version, &i.ExpectedSize, &i.ExpectedDigest, &i.SignatureAlgorithm, &i.Signature, &i.SigningKeyID, &i.MinimumSecurityVersion, &i.ReleaseNotes, &i.Status, &i.VerifiedAt, &i.CreatedAt, &i.UpdatedAt, &i.objectRef}
|
||||
return []any{&i.ID, &i.HouseholdID, &i.Model, &i.HardwareRevision, &i.Version, &i.ExpectedSize, &i.ExpectedDigest, &i.SignatureAlgorithm, &i.Signature, &i.SigningKeyID, &i.MinimumSecurityVersion, &i.ReleaseNotes, &i.ReleaseChannel, &i.Mandatory, &i.PublishedAt, &i.CreatedSource, &i.Status, &i.VerifiedAt, &i.CreatedAt, &i.UpdatedAt, &i.objectRef}
|
||||
}
|
||||
|
||||
func normalizeReleaseChannel(value string) string {
|
||||
value = strings.ToUpper(strings.TrimSpace(value))
|
||||
if value == "BETA" || value == "INTERNAL" {
|
||||
return value
|
||||
}
|
||||
return "STABLE"
|
||||
}
|
||||
|
||||
func (s *Service) ListFirmwareReleases(ctx context.Context, p identity.Principal, householdPublicID string) ([]FirmwareRelease, error) {
|
||||
@@ -346,7 +361,7 @@ func (s *Service) ListFirmwareReleases(ctx context.Context, p identity.Principal
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rows, err := tx.Query(ctx, `SELECT public_id::text,model,COALESCE(hardware_revision,''),version,expected_size,expected_digest,signature_algorithm,signature,signing_key_id,COALESCE(minimum_security_version,''),COALESCE(release_notes,''),status,verified_at,created_at,updated_at,object_ref FROM firmware_release WHERE tenant_id=$1::uuid AND household_id=$2::uuid ORDER BY created_at DESC LIMIT 100`, a.tenantID, a.householdID)
|
||||
rows, err := tx.Query(ctx, `SELECT public_id::text,COALESCE(household_id::text,''),model,COALESCE(hardware_revision,''),version,expected_size,expected_digest,signature_algorithm,signature,signing_key_id,COALESCE(minimum_security_version,''),COALESCE(release_notes,''),release_channel,mandatory,published_at,created_source,status,verified_at,created_at,updated_at,object_ref FROM firmware_release WHERE tenant_id=$1::uuid AND household_id=$2::uuid ORDER BY created_at DESC LIMIT 100`, a.tenantID, a.householdID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -698,7 +713,7 @@ func (s *Service) resolveFirmwareRelease(ctx context.Context, p identity.Princip
|
||||
var item FirmwareRelease
|
||||
var a resourceAccess
|
||||
var role string
|
||||
err := s.pool.QueryRow(ctx, `SELECT fr.public_id::text,fr.model,COALESCE(fr.hardware_revision,''),fr.version,fr.expected_size,fr.expected_digest,fr.signature_algorithm,fr.signature,fr.signing_key_id,COALESCE(fr.minimum_security_version,''),COALESCE(fr.release_notes,''),fr.status,fr.verified_at,fr.created_at,fr.updated_at,fr.object_ref,fr.tenant_id::text,ua.id::text,ua.public_id::text,hm.role,h.id::text,h.public_id::text FROM firmware_release fr JOIN household h ON h.id=fr.household_id AND h.tenant_id=fr.tenant_id JOIN household_member hm ON hm.household_id=h.id AND hm.status='active' JOIN user_account ua ON ua.id=hm.user_id AND ua.tenant_id=fr.tenant_id AND ua.status='active' WHERE fr.public_id=$1::uuid AND ua.oidc_issuer=$2 AND ua.oidc_subject=$3 LIMIT 1`, publicID, p.Issuer, p.Subject).Scan(append(firmwareReleaseTargets(&item), &a.tenantID, &a.userID, &a.userPublicID, &role, &a.householdID, &a.householdPublicID)...)
|
||||
err := s.pool.QueryRow(ctx, `SELECT fr.public_id::text,COALESCE(fr.household_id::text,''),fr.model,COALESCE(fr.hardware_revision,''),fr.version,fr.expected_size,fr.expected_digest,fr.signature_algorithm,fr.signature,fr.signing_key_id,COALESCE(fr.minimum_security_version,''),COALESCE(fr.release_notes,''),fr.release_channel,fr.mandatory,fr.published_at,fr.created_source,fr.status,fr.verified_at,fr.created_at,fr.updated_at,fr.object_ref,fr.tenant_id::text,ua.id::text,ua.public_id::text,hm.role,h.id::text,h.public_id::text FROM firmware_release fr JOIN household h ON h.id=fr.household_id AND h.tenant_id=fr.tenant_id JOIN household_member hm ON hm.household_id=h.id AND hm.status='active' JOIN user_account ua ON ua.id=hm.user_id AND ua.tenant_id=fr.tenant_id AND ua.status='active' WHERE fr.public_id=$1::uuid AND ua.oidc_issuer=$2 AND ua.oidc_subject=$3 LIMIT 1`, publicID, p.Issuer, p.Subject).Scan(append(firmwareReleaseTargets(&item), &a.tenantID, &a.userID, &a.userPublicID, &role, &a.householdID, &a.householdPublicID)...)
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return FirmwareRelease{}, resourceAccess{}, ErrAccessDenied
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user