This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
package onboarding
|
||||
|
||||
import (
|
||||
"data-terminal-cloud/internal/deviceauth"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var runtimePath = regexp.MustCompile(`^/device/v(?:2/conversations|3/agent-sessions)/([0-9a-f-]{36})(?:/(events|heartbeat|stop|activate|turns:batch|upload-batches))?$`)
|
||||
|
||||
func (s *Service) runtimeAdmission(r *http.Request, d Device, raw []byte) (bool, error) {
|
||||
create := r.URL.Path == "/device/v3/agent-sessions"
|
||||
match := runtimePath.FindStringSubmatch(r.URL.Path)
|
||||
if r.URL.Path == "/device/v2/conversations" {
|
||||
return false, deviceauth.ErrDeviceInactive
|
||||
}
|
||||
if len(match) > 0 {
|
||||
var current bool
|
||||
err := s.pool.QueryRow(r.Context(), `SELECT EXISTS(SELECT 1 FROM conversation WHERE public_id=$1::uuid AND device_id=$2::uuid AND onboarding_generation=$3)`, match[1], d.ID, d.Generation).Scan(¤t)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if !current {
|
||||
return false, deviceauth.ErrDeviceInactive
|
||||
}
|
||||
}
|
||||
if !create && len(match) == 0 {
|
||||
parts := strings.Split(r.URL.Path, "/")
|
||||
if len(parts) >= 5 && (parts[3] == "upload-batches" || parts[3] == "media-segments") {
|
||||
if !uuidValid(parts[4]) {
|
||||
return false, deviceauth.ErrCredentialInvalid
|
||||
}
|
||||
table := "upload_batch"
|
||||
if parts[3] == "media-segments" {
|
||||
table = "media_segment"
|
||||
}
|
||||
var current bool
|
||||
err := s.pool.QueryRow(r.Context(), `SELECT EXISTS(SELECT 1 FROM `+table+` m JOIN conversation c ON c.id=m.conversation_id WHERE m.public_id=$1::uuid AND c.device_id=$2::uuid AND c.onboarding_generation=$3)`, parts[4], d.ID, d.Generation).Scan(¤t)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if !current {
|
||||
return false, deviceauth.ErrDeviceInactive
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
var intent, activation, attempt, token string
|
||||
if create {
|
||||
var input struct {
|
||||
Intent string `json:"intent"`
|
||||
Activation string `json:"activation_id"`
|
||||
Attempt string `json:"attempt_id"`
|
||||
Token string `json:"permit_token"`
|
||||
}
|
||||
if err := json.Unmarshal(raw, &input); err != nil {
|
||||
return false, deviceauth.ErrCredentialInvalid
|
||||
}
|
||||
intent = input.Intent
|
||||
activation = input.Activation
|
||||
attempt = input.Attempt
|
||||
token = input.Token
|
||||
}
|
||||
var ready bool
|
||||
err := s.pool.QueryRow(r.Context(), `SELECT EXISTS(SELECT 1 FROM device_activation_job j JOIN device_binding b ON b.device_id=j.device_id AND b.generation=j.binding_generation AND b.unbound_at IS NULL WHERE j.device_id=$1::uuid AND j.binding_generation=$2 AND j.status='READY' AND j.config_ack_at IS NOT NULL AND j.config_expires_at>now())`, d.ID, d.Generation).Scan(&ready)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if ready && intent != "activation_test" {
|
||||
return false, nil
|
||||
}
|
||||
var valid bool
|
||||
if create {
|
||||
if intent != "activation_test" || !uuidV4(activation) || !uuidV4(attempt) || (token != "" && !validB64(token, 32)) {
|
||||
return false, deviceauth.ErrDeviceInactive
|
||||
}
|
||||
err = s.pool.QueryRow(r.Context(), `SELECT EXISTS(SELECT 1 FROM device_activation_job j JOIN device_activation_attempt a ON a.activation_id=j.id JOIN device_binding b ON b.device_id=j.device_id AND b.generation=j.binding_generation AND b.unbound_at IS NULL WHERE j.device_id=$1::uuid AND j.id=$2::uuid AND a.id=$3::uuid AND j.binding_generation=$4 AND a.binding_generation=$4 AND a.config_version=j.config_version AND j.status IN ('REGISTERED_OBSERVED','CONTACT_POSSIBLE','READY') AND j.config_ack_at IS NOT NULL AND j.engine_ready_at IS NOT NULL AND j.config_expires_at>now() AND a.stage='CONVERSATION_TEST' AND a.executor='DEVICE' AND a.status='CONTACT_POSSIBLE' AND (a.permit_hash IS NULL OR a.permit_hash=$5) AND (a.permit_expires_at>now() OR a.conversation_id IS NOT NULL))`, d.ID, activation, attempt, d.Generation, digest([]byte(token))).Scan(&valid)
|
||||
} else {
|
||||
if len(match) > 2 && !oneOf(match[2], "", "events", "heartbeat", "stop") {
|
||||
return false, deviceauth.ErrDeviceInactive
|
||||
}
|
||||
err = s.pool.QueryRow(r.Context(), `SELECT EXISTS(SELECT 1 FROM conversation c JOIN device_activation_attempt a ON a.conversation_id=c.id JOIN device_activation_job j ON j.id=a.activation_id WHERE c.public_id=$1::uuid AND c.device_id=$2::uuid AND j.binding_generation=$3 AND a.binding_generation=$3 AND a.config_version=j.config_version AND a.stage='CONVERSATION_TEST' AND a.status IN ('CONTACT_POSSIBLE','SUCCESS_OBSERVED','UNKNOWN'))`, match[1], d.ID, d.Generation).Scan(&valid)
|
||||
}
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if !valid {
|
||||
return false, deviceauth.ErrDeviceInactive
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
Reference in New Issue
Block a user