feat: complete device onboarding and mobile binding workflows
ci / verify (push) Canceled after 0s

This commit is contained in:
Codex Local
2026-09-12 08:57:29 +08:00
parent 7846f30d0b
commit 98fb2965d0
116 changed files with 15669 additions and 64 deletions
+19
View File
@@ -15,6 +15,7 @@ import (
"data-terminal-cloud/internal/audit"
"data-terminal-cloud/internal/deviceactivation"
"data-terminal-cloud/internal/deviceauth"
"data-terminal-cloud/internal/devicebinding"
"data-terminal-cloud/internal/identity"
"github.com/google/uuid"
@@ -398,6 +399,9 @@ func (s *Service) IssueDeviceClaim(ctx context.Context, principal identity.Princ
}
func (s *Service) issueDeviceClaimTx(ctx context.Context, tx pgx.Tx, access operatorAccess, devicePublicID, deviceID, requestID string) (DeviceClaimIssue, error) {
if err := devicebinding.RequireLegacy(ctx, tx, deviceID); err != nil {
return DeviceClaimIssue{}, err
}
randomBytes := make([]byte, 10)
if _, err := rand.Read(randomBytes); err != nil {
return DeviceClaimIssue{}, fmt.Errorf("generate device claim: %w", err)
@@ -453,6 +457,11 @@ func (s *Service) Update(ctx context.Context, principal identity.Principal, tena
return Device{}, ErrVersionConflict
}
model, modelSet := pointerValue(input.Model)
if input.Model != nil || input.HardwareRevision != nil || input.VoiceRuntime != nil || (input.HouseholdID != nil && strings.TrimSpace(*input.HouseholdID) != "") {
if err := devicebinding.RequireLegacy(ctx, tx, deviceID); err != nil {
return Device{}, err
}
}
hardware, hardwareSet := pointerValue(input.HardwareRevision)
security, securitySet := pointerValue(input.MinimumSecurityVersion)
firmware, firmwareSet := pointerValue(input.MinimumFirmwareVersion)
@@ -735,6 +744,13 @@ func (s *Service) RotateCredential(ctx context.Context, principal identity.Princ
if err != nil {
return CredentialIssue{}, err
}
var rotationDeviceID string
if err := tx.QueryRow(ctx, `SELECT id::text FROM device WHERE public_id=$1::uuid AND tenant_id=$2::uuid FOR UPDATE`, devicePublicID, access.tenantID).Scan(&rotationDeviceID); err != nil {
return CredentialIssue{}, ErrNotFound
}
if err := devicebinding.RequireLegacy(ctx, tx, rotationDeviceID); err != nil {
return CredentialIssue{}, err
}
version, err := s.credential.ProvisionTx(ctx, tx, devicePublicID, []byte(secret), true)
if errors.Is(err, deviceauth.ErrDeviceNotFound) {
return CredentialIssue{}, ErrNotFound
@@ -786,6 +802,9 @@ func validateAssignment(ctx context.Context, q queryRow, tenantID, householdPubl
}
func insertBinding(ctx context.Context, tx pgx.Tx, access operatorAccess, deviceID, householdPublicID, subjectPublicID, reason string) error {
if err := devicebinding.RequireLegacy(ctx, tx, deviceID); err != nil {
return err
}
_, err := tx.Exec(ctx, `INSERT INTO device_binding(tenant_id,device_id,household_id,subject_id,bound_by,reason)
SELECT $1::uuid,$2::uuid,h.id,sp.id,$5::uuid,$6 FROM household h JOIN subject_profile sp ON sp.household_id=h.id AND sp.tenant_id=h.tenant_id
WHERE h.public_id=$3::uuid AND sp.public_id=$4::uuid AND h.tenant_id=$1::uuid`,