38 lines
1.5 KiB
Go
38 lines
1.5 KiB
Go
package onboarding
|
|||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
"github.com/jackc/pgx/v5"
|
||
|
|
"strings"
|
||
|
|
)
|
||
|
|
|
||
|
|
// Validate the exact cached resource, never a replacement with similar inputs.
|
||
|
|
func (s *Service) replayResultGuard(ctx context.Context, tx pgx.Tx, d Device, actor, scope string, result map[string]any) error {
|
||
|
|
var valid bool
|
||
|
|
var err error
|
||
|
|
switch scope {
|
||
|
|
case "handoff":
|
||
|
|
token, _ := result["handoff_token"].(string)
|
||
|
|
err = tx.QueryRow(ctx, `SELECT EXISTS(SELECT 1 FROM device_handoff WHERE token_hash=$1 AND consumed_at IS NULL AND expires_at>now())`, digest([]byte(token))).Scan(&valid)
|
||
|
|
case "claim":
|
||
|
|
id, _ := result["claim_id"].(string)
|
||
|
|
err = tx.QueryRow(ctx, `SELECT EXISTS(SELECT 1 FROM device_binding_claim WHERE id=$1::uuid AND device_id=$2::uuid AND status='PENDING' AND expires_at>now())`, id, d.ID).Scan(&valid)
|
||
|
|
case "profile":
|
||
|
|
id, _ := result["elder_id"].(string)
|
||
|
|
w, e := s.webSession(ctx, tx, strings.TrimPrefix(actor, "web:"))
|
||
|
|
if e != nil {
|
||
|
|
return e
|
||
|
|
}
|
||
|
|
err = tx.QueryRow(ctx, `SELECT EXISTS(SELECT 1 FROM subject_profile s JOIN household h ON h.id=s.household_id JOIN household_member m ON m.household_id=h.id WHERE s.public_id=$1::uuid AND s.tenant_id=$2::uuid AND h.status='active' AND m.user_id=$3::uuid AND m.status='active' AND m.role IN ('owner','admin','editor'))`, id, d.TenantID, w.UserID).Scan(&valid)
|
||
|
|
default:
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
if !valid {
|
||
|
|
return fail(410, "REQUEST_RESOURCE_EXPIRED", "原请求对应资源已失效,请恢复当前流程")
|
||
|
|
}
|
||
|
|
return nil
|
||
|
|
}
|