2026-08-26 06:41:07 +08:00
|
|
|
package httpapi
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"errors"
|
|
|
|
|
"net/http"
|
|
|
|
|
|
|
|
|
|
"data-terminal-cloud/internal/deviceauth"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type DeviceRequestVerifier interface {
|
|
|
|
|
Verify(*http.Request) (deviceauth.Identity, error)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func DeviceAuth(verifier DeviceRequestVerifier, next http.Handler) http.Handler {
|
|
|
|
|
return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
|
|
|
|
|
identity, err := verifier.Verify(request)
|
|
|
|
|
if err != nil {
|
|
|
|
|
if errors.Is(err, deviceauth.ErrCredentialsMissing) ||
|
|
|
|
|
errors.Is(err, deviceauth.ErrCredentialInvalid) ||
|
|
|
|
|
errors.Is(err, deviceauth.ErrDeviceInactive) ||
|
|
|
|
|
errors.Is(err, deviceauth.ErrRequestExpired) ||
|
|
|
|
|
errors.Is(err, deviceauth.ErrReplayDetected) {
|
|
|
|
|
WriteError(writer, request, http.StatusUnauthorized, "DEVICE_AUTHENTICATION_FAILED", "设备身份验证失败")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
WriteAPIError(writer, request, http.StatusServiceUnavailable, Error{
|
|
|
|
|
Code: "DEVICE_AUTHENTICATION_UNAVAILABLE", Message: "设备身份服务暂时不可用", Retryable: true,
|
|
|
|
|
})
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
next.ServeHTTP(writer, request.WithContext(deviceauth.WithIdentity(request.Context(), identity)))
|
|
|
|
|
})
|
|
|
|
|
}
|
2026-09-06 17:04:11 +08:00
|
|
|
|
|
|
|
|
func RequireDeviceActivation(allowed []string, next http.Handler) http.Handler {
|
|
|
|
|
allowedSet := make(map[string]struct{}, len(allowed))
|
|
|
|
|
for _, state := range allowed {
|
|
|
|
|
allowedSet[state] = struct{}{}
|
|
|
|
|
}
|
|
|
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
identity, ok := deviceauth.FromContext(r.Context())
|
|
|
|
|
if !ok {
|
|
|
|
|
WriteError(w, r, http.StatusUnauthorized, "DEVICE_AUTHENTICATION_FAILED", "设备身份验证失败")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
state := identity.ActivationState
|
|
|
|
|
if state == "" {
|
|
|
|
|
state = "ACTIVE"
|
|
|
|
|
}
|
2026-09-12 08:57:29 +08:00
|
|
|
if _, ok := allowedSet[state]; !ok && !(identity.EnrollmentMode == "physical_confirm" && identity.ActivationTest) {
|
2026-09-06 17:04:11 +08:00
|
|
|
WriteAPIError(w, r, http.StatusConflict, Error{Code: "DEVICE_ACTIVATION_REQUIRED", Message: "当前设备激活状态不能执行该操作", Details: map[string]any{"activation_state": state, "allowed_states": allowed}})
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
next.ServeHTTP(w, r)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func RequireHardwareLicense(next http.Handler) http.Handler {
|
|
|
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
identity, ok := deviceauth.FromContext(r.Context())
|
|
|
|
|
if !ok {
|
|
|
|
|
WriteError(w, r, http.StatusUnauthorized, "DEVICE_AUTHENTICATION_FAILED", "设备身份验证失败")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if identity.VoiceRuntime == "volc_hardware_agent" && !identity.HardwareLicenseReady {
|
|
|
|
|
WriteError(w, r, http.StatusConflict, "HARDWARE_LICENSE_REQUIRED", "设备没有已激活且未到期的硬件智能体License")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
next.ServeHTTP(w, r)
|
|
|
|
|
})
|
|
|
|
|
}
|