models.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608
  1. package store
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "errors"
  6. "io"
  7. "reflect"
  8. "sort"
  9. "strconv"
  10. "strings"
  11. "time"
  12. )
  13. const SecretMask = "********"
  14. type Device struct {
  15. ID string
  16. Name string
  17. Interface string
  18. ControlDevice string
  19. ATPort string
  20. USBPath string
  21. AudioDevice string
  22. ModemIMEI string
  23. APN string
  24. ProxyPort int
  25. BaudRate int
  26. DataBits int
  27. StopBits int
  28. Parity string
  29. DeviceBackend string
  30. ESIMTransport string
  31. QMIUseProxy bool
  32. QMIProxyPath string
  33. QMIProxyExecutable string
  34. NetworkEnabled bool
  35. SMSEnabled bool
  36. VoWiFiEnabled bool
  37. Extra json.RawMessage
  38. CreatedAt time.Time
  39. UpdatedAt time.Time
  40. }
  41. type DeviceRuntime struct {
  42. DeviceID string
  43. Running bool
  44. Healthy bool
  45. ControlOnline bool
  46. PhysicalPresent bool
  47. WorkerRunning bool
  48. DataConnected bool
  49. RadioRegistered bool
  50. NetworkConnected bool
  51. FlightMode bool
  52. LifecyclePhase string
  53. LifecycleReason string
  54. PublicIP string
  55. PrivateIP string
  56. Operator string
  57. NativeMCC string
  58. NativeMNC string
  59. NativeSPN string
  60. NetworkMode string
  61. NetworkDuplex string
  62. RadioBand string
  63. RadioChannel int
  64. SignalDBM int
  65. SignalRSRP *int
  66. SignalRSRQ *int
  67. SignalSINR *int
  68. IMEI string
  69. ICCID string
  70. IMSI string
  71. Firmware string
  72. RegStatus int
  73. RegStatusText string
  74. PSAttached *bool
  75. SIMInserted *bool
  76. OperatingMode *int
  77. PhoneNumber string
  78. PhoneNumberSource string
  79. Traffic json.RawMessage
  80. Extra json.RawMessage
  81. UpdatedAt time.Time
  82. }
  83. type VoWiFiRuntime struct {
  84. DeviceID string
  85. Phase string
  86. DataplaneMode string
  87. ICCID string
  88. IMSI string
  89. SIMReady bool
  90. AccessReady bool
  91. TunnelReady bool
  92. IMSReady bool
  93. SMSReady bool
  94. RegStatus int
  95. RegStatusText string
  96. NetworkMode string
  97. LocalPhone string
  98. PhoneNumberSource string
  99. LastErrorClass string
  100. LastError string
  101. LastReason string
  102. Tunnel json.RawMessage
  103. IMSCore json.RawMessage
  104. SMSIP json.RawMessage
  105. Extra json.RawMessage
  106. UpdatedAt time.Time
  107. }
  108. // PhoneAssociation is a number explicitly published by IMS for one SIM. It is
  109. // keyed by ICCID so a verified number survives service restarts and follows the
  110. // SIM without ever being inferred from IMSI.
  111. type PhoneAssociation struct {
  112. ICCID string
  113. DeviceID string
  114. Number string
  115. Source string
  116. CreatedAt time.Time
  117. UpdatedAt time.Time
  118. }
  119. type SMSMessage struct {
  120. ID int64
  121. MessageID string
  122. DeviceID string
  123. IMSI string
  124. Peer string
  125. Direction string
  126. Body string
  127. Timestamp time.Time
  128. Status string
  129. Source string
  130. PartsTotal int
  131. DeliveryState string
  132. Read bool
  133. Extra json.RawMessage
  134. CreatedAt time.Time
  135. UpdatedAt time.Time
  136. }
  137. type SMSFilter struct {
  138. DeviceID string
  139. IMSI string
  140. Peer string
  141. Since time.Time
  142. Until time.Time
  143. BeforeID int64
  144. Limit int
  145. }
  146. // SMSDeliveryReport is network evidence for one submitted SMS part. The
  147. // message reference is the TP-MR returned in SMS-STATUS-REPORT.
  148. type SMSDeliveryReport struct {
  149. DeviceID string
  150. IMSI string
  151. Peer string
  152. Source string
  153. MessageReference int
  154. StatusCode int
  155. DeliveryState string
  156. ServiceCenterTime *time.Time
  157. DischargeTime *time.Time
  158. ReceivedAt time.Time
  159. }
  160. type SMSContact struct {
  161. DeviceID string
  162. DeviceName string
  163. IMSI string
  164. LocalPhone string
  165. Peer string
  166. DisplayName string
  167. LastMessage string
  168. LastTimestamp time.Time
  169. Direction string
  170. LastSMSID int64
  171. UnreadCount int
  172. MessageCount int
  173. }
  174. type LocalProxyConfig struct {
  175. ID string
  176. Name string
  177. Mode string
  178. DeviceID string
  179. ListenAddr string
  180. ListenPort int
  181. Enabled bool
  182. AuthEnabled bool
  183. Username string
  184. Password string
  185. Extra json.RawMessage
  186. CreatedAt time.Time
  187. UpdatedAt time.Time
  188. }
  189. func (value LocalProxyConfig) Redacted() LocalProxyConfig {
  190. if value.Password != "" {
  191. value.Password = SecretMask
  192. }
  193. return value
  194. }
  195. func (value LocalProxyConfig) Public() LocalProxyConfig {
  196. value.Password = ""
  197. return value
  198. }
  199. func (value LocalProxyConfig) SensitiveValues() []string {
  200. if value.Password == "" || value.Password == SecretMask {
  201. return nil
  202. }
  203. return []string{value.Password}
  204. }
  205. type UpstreamProxy struct {
  206. ID string
  207. Name string
  208. Addr string
  209. Username string
  210. Password string
  211. Enabled bool
  212. Extra json.RawMessage
  213. CreatedAt time.Time
  214. UpdatedAt time.Time
  215. }
  216. func (value UpstreamProxy) Redacted() UpstreamProxy {
  217. if value.Password != "" {
  218. value.Password = SecretMask
  219. }
  220. return value
  221. }
  222. func (value UpstreamProxy) Public() UpstreamProxy {
  223. value.Password = ""
  224. return value
  225. }
  226. func (value UpstreamProxy) SensitiveValues() []string {
  227. if value.Password == "" || value.Password == SecretMask {
  228. return nil
  229. }
  230. return []string{value.Password}
  231. }
  232. type CountryRule struct {
  233. CountryCode string
  234. CountryName string
  235. UpstreamProxyID string
  236. Enabled bool
  237. Extra json.RawMessage
  238. CreatedAt time.Time
  239. UpdatedAt time.Time
  240. }
  241. // DeviceProxyBinding selects the SOCKS5 upstream used by one device's whole
  242. // VoWiFi runtime. The IKE/IPsec transport uses this route and IMS/SMS then
  243. // travel inside that tunnel.
  244. type DeviceProxyBinding struct {
  245. DeviceID string
  246. UpstreamProxyID string
  247. CreatedAt time.Time
  248. UpdatedAt time.Time
  249. }
  250. type NotificationSetting struct {
  251. Channel string
  252. Enabled bool
  253. Config json.RawMessage
  254. SensitiveFields []string
  255. CreatedAt time.Time
  256. UpdatedAt time.Time
  257. }
  258. func (value NotificationSetting) Redacted() NotificationSetting {
  259. value.Config = redactJSONFields(value.Config, value.SensitiveFields, SecretMask)
  260. return value
  261. }
  262. func (value NotificationSetting) Public() NotificationSetting {
  263. value.Config = redactJSONFields(value.Config, value.SensitiveFields, "")
  264. return value
  265. }
  266. func (value NotificationSetting) SensitiveValues() []string {
  267. document, err := decodeJSONObject(value.Config)
  268. if err != nil {
  269. return nil
  270. }
  271. values := make([]string, 0, len(value.SensitiveFields))
  272. for _, field := range value.SensitiveFields {
  273. if secret, ok := getJSONPath(document, field).(string); ok &&
  274. secret != "" && secret != SecretMask {
  275. values = append(values, secret)
  276. }
  277. }
  278. return values
  279. }
  280. type AppSetting struct {
  281. Key string
  282. Value json.RawMessage
  283. Sensitive bool
  284. UpdatedAt time.Time
  285. }
  286. func (value AppSetting) Redacted() AppSetting {
  287. if value.Sensitive {
  288. value.Value = json.RawMessage(strconv.Quote(SecretMask))
  289. }
  290. return value
  291. }
  292. func (value AppSetting) Public() AppSetting {
  293. if value.Sensitive {
  294. value.Value = json.RawMessage(`null`)
  295. }
  296. return value
  297. }
  298. func (value AppSetting) SensitiveValues() []string {
  299. if !value.Sensitive {
  300. return nil
  301. }
  302. var decoded any
  303. decoder := json.NewDecoder(bytes.NewReader(value.Value))
  304. decoder.UseNumber()
  305. if decoder.Decode(&decoded) != nil {
  306. return nil
  307. }
  308. var values []string
  309. collectJSONStringValues(decoded, &values)
  310. return uniqueNonemptyStrings(values)
  311. }
  312. type SensitiveValuesProvider interface {
  313. SensitiveValues() []string
  314. }
  315. func RedactText(text string, providers ...SensitiveValuesProvider) string {
  316. var secrets []string
  317. for _, provider := range providers {
  318. if !nilSensitiveProvider(provider) {
  319. secrets = append(secrets, provider.SensitiveValues()...)
  320. }
  321. }
  322. sort.Slice(secrets, func(i, j int) bool {
  323. return len(secrets[i]) > len(secrets[j])
  324. })
  325. for _, secret := range secrets {
  326. if secret != "" && secret != SecretMask {
  327. text = strings.ReplaceAll(text, secret, SecretMask)
  328. }
  329. }
  330. return text
  331. }
  332. func nilSensitiveProvider(provider SensitiveValuesProvider) bool {
  333. if provider == nil {
  334. return true
  335. }
  336. value := reflect.ValueOf(provider)
  337. switch value.Kind() {
  338. case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map,
  339. reflect.Pointer, reflect.Slice:
  340. return value.IsNil()
  341. default:
  342. return false
  343. }
  344. }
  345. func collectJSONStringValues(value any, result *[]string) {
  346. switch typed := value.(type) {
  347. case string:
  348. if typed != "" && typed != SecretMask {
  349. *result = append(*result, typed)
  350. }
  351. case []any:
  352. for _, item := range typed {
  353. collectJSONStringValues(item, result)
  354. }
  355. case map[string]any:
  356. for _, item := range typed {
  357. collectJSONStringValues(item, result)
  358. }
  359. }
  360. }
  361. type AuditEvent struct {
  362. ID int64
  363. Actor string
  364. Action string
  365. EntityType string
  366. EntityID string
  367. Outcome string
  368. RemoteAddr string
  369. Details json.RawMessage
  370. CreatedAt time.Time
  371. }
  372. type AuditFilter struct {
  373. Actor string
  374. Action string
  375. EntityType string
  376. EntityID string
  377. Since time.Time
  378. Until time.Time
  379. BeforeID int64
  380. Limit int
  381. }
  382. type LogEvent struct {
  383. ID int64
  384. Time time.Time
  385. Level string
  386. Message string
  387. Caller string
  388. Fields json.RawMessage
  389. }
  390. type LogFilter struct {
  391. Level string
  392. Since time.Time
  393. Until time.Time
  394. BeforeID int64
  395. Limit int
  396. }
  397. type CardPolicy struct {
  398. ICCID string
  399. NetworkEnabled bool
  400. VoWiFiEnabled bool
  401. AirplaneEnabled bool
  402. APN string
  403. IPVersion string
  404. Source string
  405. CreatedAt time.Time
  406. UpdatedAt time.Time
  407. }
  408. type TrafficBucket struct {
  409. DeviceID string
  410. Bucket string
  411. PeriodStart time.Time
  412. RXBytes int64
  413. TXBytes int64
  414. }
  415. func (value TrafficBucket) TotalBytes() int64 {
  416. return value.RXBytes + value.TXBytes
  417. }
  418. type TrafficFilter struct {
  419. DeviceID string
  420. Bucket string
  421. Since time.Time
  422. Until time.Time
  423. Limit int
  424. }
  425. func normalizeJSONObject(value json.RawMessage) (json.RawMessage, error) {
  426. if len(bytes.TrimSpace(value)) == 0 {
  427. return json.RawMessage(`{}`), nil
  428. }
  429. document, err := decodeJSONObject(value)
  430. if err != nil {
  431. return nil, err
  432. }
  433. normalized, err := json.Marshal(document)
  434. if err != nil {
  435. return nil, err
  436. }
  437. return normalized, nil
  438. }
  439. func normalizeJSONValue(value json.RawMessage) (json.RawMessage, error) {
  440. if len(bytes.TrimSpace(value)) == 0 {
  441. return json.RawMessage(`null`), nil
  442. }
  443. if !json.Valid(value) {
  444. return nil, errors.New("invalid JSON value")
  445. }
  446. return append(json.RawMessage(nil), value...), nil
  447. }
  448. func decodeJSONObject(value json.RawMessage) (map[string]any, error) {
  449. var document map[string]any
  450. decoder := json.NewDecoder(bytes.NewReader(value))
  451. decoder.UseNumber()
  452. if err := decoder.Decode(&document); err != nil {
  453. return nil, err
  454. }
  455. var trailing any
  456. if err := decoder.Decode(&trailing); !errors.Is(err, io.EOF) {
  457. if err == nil {
  458. return nil, errors.New("JSON value must contain exactly one object")
  459. }
  460. return nil, err
  461. }
  462. if document == nil {
  463. return nil, errors.New("JSON value must be an object")
  464. }
  465. return document, nil
  466. }
  467. func redactJSONFields(value json.RawMessage, fields []string, replacement string) json.RawMessage {
  468. document, err := decodeJSONObject(value)
  469. if err != nil {
  470. return json.RawMessage(`{}`)
  471. }
  472. for _, field := range fields {
  473. if getJSONPath(document, field) != nil {
  474. setJSONPath(document, field, replacement)
  475. }
  476. }
  477. encoded, err := json.Marshal(document)
  478. if err != nil {
  479. return json.RawMessage(`{}`)
  480. }
  481. return encoded
  482. }
  483. func mergeJSONSecrets(
  484. incoming json.RawMessage,
  485. existing json.RawMessage,
  486. fields []string,
  487. ) (json.RawMessage, error) {
  488. next, err := decodeJSONObject(incoming)
  489. if err != nil {
  490. return nil, err
  491. }
  492. current, err := decodeJSONObject(existing)
  493. if err != nil {
  494. current = map[string]any{}
  495. }
  496. for _, field := range fields {
  497. value := getJSONPath(next, field)
  498. text, stringValue := value.(string)
  499. if value == nil || (stringValue && (text == "" || text == SecretMask)) {
  500. if previous := getJSONPath(current, field); previous != nil {
  501. setJSONPath(next, field, previous)
  502. }
  503. }
  504. }
  505. return json.Marshal(next)
  506. }
  507. func getJSONPath(document map[string]any, path string) any {
  508. if strings.TrimSpace(path) == "" {
  509. return nil
  510. }
  511. parts := strings.Split(path, ".")
  512. var current any = document
  513. for _, part := range parts {
  514. object, ok := current.(map[string]any)
  515. if !ok {
  516. return nil
  517. }
  518. current, ok = object[part]
  519. if !ok {
  520. return nil
  521. }
  522. }
  523. return current
  524. }
  525. func setJSONPath(document map[string]any, path string, value any) {
  526. if strings.TrimSpace(path) == "" {
  527. return
  528. }
  529. parts := strings.Split(path, ".")
  530. current := document
  531. for _, part := range parts[:len(parts)-1] {
  532. next, ok := current[part].(map[string]any)
  533. if !ok {
  534. next = map[string]any{}
  535. current[part] = next
  536. }
  537. current = next
  538. }
  539. current[parts[len(parts)-1]] = value
  540. }
  541. func boolInt(value bool) int {
  542. if value {
  543. return 1
  544. }
  545. return 0
  546. }
  547. func nullableBool(value *bool) any {
  548. if value == nil {
  549. return nil
  550. }
  551. return boolInt(*value)
  552. }
  553. func nullableInt(value *int) any {
  554. if value == nil {
  555. return nil
  556. }
  557. return *value
  558. }