2026-09-12 08:57:29 +08:00
// Package devicebinding owns compatibility gates shared by all ownership writers.
// Generation advancement and claim fencing are enforced by the database trigger
// so background deletion and operator mutations cannot omit invalidation.
package devicebinding
import (
"context"
"errors"
"github.com/jackc/pgx/v5"
)
var ErrConfirmationRequired = errors . New ( "device physical confirmation workflow required" )
type Queryer interface {
QueryRow ( context . Context , string , ... any ) pgx . Row
}
func RequireLegacy ( ctx context . Context , q Queryer , deviceID string ) error {
var mode string
err := q . QueryRow ( ctx , `SELECT enrollment_mode FROM device WHERE id=$1::uuid` , deviceID ). Scan ( & mode )
if err != nil {
return err
}
if mode != "legacy" {
return ErrConfirmationRequired
}
return nil
}
func RequireUnboundForReset ( ctx context . Context , q Queryer , deviceID string ) error {
var allowed bool
2026-10-08 10:06:44 +08:00
err := q . QueryRow ( ctx , `SELECT enrollment_mode='legacy' OR (NOT EXISTS(SELECT 1 FROM device_binding WHERE device_id=d.id AND unbound_at IS NULL) AND NOT EXISTS(SELECT 1 FROM device_account_binding WHERE device_id=d.id AND released_at IS NULL)) FROM device d WHERE id=$1::uuid` , deviceID ). Scan ( & allowed )
2026-09-12 08:57:29 +08:00
if err != nil {
return err
}
if ! allowed {
return ErrConfirmationRequired
}
return nil
}