syspolicy_test.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. package syspolicy
  4. import (
  5. "errors"
  6. "slices"
  7. "testing"
  8. "time"
  9. "tailscale.com/util/syspolicy/setting"
  10. )
  11. // testHandler encompasses all data types returned when testing any of the syspolicy
  12. // methods that involve getting a policy value.
  13. // For keys and the corresponding values, check policy_keys.go.
  14. type testHandler struct {
  15. t *testing.T
  16. key Key
  17. s string
  18. u64 uint64
  19. b bool
  20. sArr []string
  21. err error
  22. calls int // used for testing reads from cache vs. handler
  23. }
  24. var someOtherError = errors.New("error other than not found")
  25. func (th *testHandler) ReadString(key string) (string, error) {
  26. if key != string(th.key) {
  27. th.t.Errorf("ReadString(%q) want %q", key, th.key)
  28. }
  29. th.calls++
  30. return th.s, th.err
  31. }
  32. func (th *testHandler) ReadUInt64(key string) (uint64, error) {
  33. if key != string(th.key) {
  34. th.t.Errorf("ReadUint64(%q) want %q", key, th.key)
  35. }
  36. th.calls++
  37. return th.u64, th.err
  38. }
  39. func (th *testHandler) ReadBoolean(key string) (bool, error) {
  40. if key != string(th.key) {
  41. th.t.Errorf("ReadBool(%q) want %q", key, th.key)
  42. }
  43. th.calls++
  44. return th.b, th.err
  45. }
  46. func (th *testHandler) ReadStringArray(key string) ([]string, error) {
  47. if key != string(th.key) {
  48. th.t.Errorf("ReadStringArray(%q) want %q", key, th.key)
  49. }
  50. th.calls++
  51. return th.sArr, th.err
  52. }
  53. func TestGetString(t *testing.T) {
  54. tests := []struct {
  55. name string
  56. key Key
  57. handlerValue string
  58. handlerError error
  59. defaultValue string
  60. wantValue string
  61. wantError error
  62. }{
  63. {
  64. name: "read existing value",
  65. key: AdminConsoleVisibility,
  66. handlerValue: "hide",
  67. wantValue: "hide",
  68. },
  69. {
  70. name: "read non-existing value",
  71. key: EnableServerMode,
  72. handlerError: ErrNoSuchKey,
  73. wantError: nil,
  74. },
  75. {
  76. name: "read non-existing value, non-blank default",
  77. key: EnableServerMode,
  78. handlerError: ErrNoSuchKey,
  79. defaultValue: "test",
  80. wantValue: "test",
  81. wantError: nil,
  82. },
  83. {
  84. name: "reading value returns other error",
  85. key: NetworkDevicesVisibility,
  86. handlerError: someOtherError,
  87. wantError: someOtherError,
  88. },
  89. }
  90. for _, tt := range tests {
  91. t.Run(tt.name, func(t *testing.T) {
  92. SetHandlerForTest(t, &testHandler{
  93. t: t,
  94. key: tt.key,
  95. s: tt.handlerValue,
  96. err: tt.handlerError,
  97. })
  98. value, err := GetString(tt.key, tt.defaultValue)
  99. if err != tt.wantError {
  100. t.Errorf("err=%q, want %q", err, tt.wantError)
  101. }
  102. if value != tt.wantValue {
  103. t.Errorf("value=%v, want %v", value, tt.wantValue)
  104. }
  105. })
  106. }
  107. }
  108. func TestGetUint64(t *testing.T) {
  109. tests := []struct {
  110. name string
  111. key Key
  112. handlerValue uint64
  113. handlerError error
  114. defaultValue uint64
  115. wantValue uint64
  116. wantError error
  117. }{
  118. {
  119. name: "read existing value",
  120. key: KeyExpirationNoticeTime,
  121. handlerValue: 1,
  122. wantValue: 1,
  123. },
  124. {
  125. name: "read non-existing value",
  126. key: LogSCMInteractions,
  127. handlerValue: 0,
  128. handlerError: ErrNoSuchKey,
  129. wantValue: 0,
  130. },
  131. {
  132. name: "read non-existing value, non-zero default",
  133. key: LogSCMInteractions,
  134. defaultValue: 2,
  135. handlerError: ErrNoSuchKey,
  136. wantValue: 2,
  137. },
  138. {
  139. name: "reading value returns other error",
  140. key: FlushDNSOnSessionUnlock,
  141. handlerError: someOtherError,
  142. wantError: someOtherError,
  143. },
  144. }
  145. for _, tt := range tests {
  146. t.Run(tt.name, func(t *testing.T) {
  147. SetHandlerForTest(t, &testHandler{
  148. t: t,
  149. key: tt.key,
  150. u64: tt.handlerValue,
  151. err: tt.handlerError,
  152. })
  153. value, err := GetUint64(tt.key, tt.defaultValue)
  154. if err != tt.wantError {
  155. t.Errorf("err=%q, want %q", err, tt.wantError)
  156. }
  157. if value != tt.wantValue {
  158. t.Errorf("value=%v, want %v", value, tt.wantValue)
  159. }
  160. })
  161. }
  162. }
  163. func TestGetBoolean(t *testing.T) {
  164. tests := []struct {
  165. name string
  166. key Key
  167. handlerValue bool
  168. handlerError error
  169. defaultValue bool
  170. wantValue bool
  171. wantError error
  172. }{
  173. {
  174. name: "read existing value",
  175. key: FlushDNSOnSessionUnlock,
  176. handlerValue: true,
  177. wantValue: true,
  178. },
  179. {
  180. name: "read non-existing value",
  181. key: LogSCMInteractions,
  182. handlerValue: false,
  183. handlerError: ErrNoSuchKey,
  184. wantValue: false,
  185. },
  186. {
  187. name: "reading value returns other error",
  188. key: FlushDNSOnSessionUnlock,
  189. handlerError: someOtherError,
  190. wantError: someOtherError,
  191. defaultValue: true,
  192. wantValue: false,
  193. },
  194. }
  195. for _, tt := range tests {
  196. t.Run(tt.name, func(t *testing.T) {
  197. SetHandlerForTest(t, &testHandler{
  198. t: t,
  199. key: tt.key,
  200. b: tt.handlerValue,
  201. err: tt.handlerError,
  202. })
  203. value, err := GetBoolean(tt.key, tt.defaultValue)
  204. if err != tt.wantError {
  205. t.Errorf("err=%q, want %q", err, tt.wantError)
  206. }
  207. if value != tt.wantValue {
  208. t.Errorf("value=%v, want %v", value, tt.wantValue)
  209. }
  210. })
  211. }
  212. }
  213. func TestGetPreferenceOption(t *testing.T) {
  214. tests := []struct {
  215. name string
  216. key Key
  217. handlerValue string
  218. handlerError error
  219. wantValue setting.PreferenceOption
  220. wantError error
  221. }{
  222. {
  223. name: "always by policy",
  224. key: EnableIncomingConnections,
  225. handlerValue: "always",
  226. wantValue: setting.AlwaysByPolicy,
  227. },
  228. {
  229. name: "never by policy",
  230. key: EnableIncomingConnections,
  231. handlerValue: "never",
  232. wantValue: setting.NeverByPolicy,
  233. },
  234. {
  235. name: "use default",
  236. key: EnableIncomingConnections,
  237. handlerValue: "",
  238. wantValue: setting.ShowChoiceByPolicy,
  239. },
  240. {
  241. name: "read non-existing value",
  242. key: EnableIncomingConnections,
  243. handlerError: ErrNoSuchKey,
  244. wantValue: setting.ShowChoiceByPolicy,
  245. },
  246. {
  247. name: "other error is returned",
  248. key: EnableIncomingConnections,
  249. handlerError: someOtherError,
  250. wantValue: setting.ShowChoiceByPolicy,
  251. wantError: someOtherError,
  252. },
  253. }
  254. for _, tt := range tests {
  255. t.Run(tt.name, func(t *testing.T) {
  256. SetHandlerForTest(t, &testHandler{
  257. t: t,
  258. key: tt.key,
  259. s: tt.handlerValue,
  260. err: tt.handlerError,
  261. })
  262. option, err := GetPreferenceOption(tt.key)
  263. if err != tt.wantError {
  264. t.Errorf("err=%q, want %q", err, tt.wantError)
  265. }
  266. if option != tt.wantValue {
  267. t.Errorf("option=%v, want %v", option, tt.wantValue)
  268. }
  269. })
  270. }
  271. }
  272. func TestGetVisibility(t *testing.T) {
  273. tests := []struct {
  274. name string
  275. key Key
  276. handlerValue string
  277. handlerError error
  278. wantValue setting.Visibility
  279. wantError error
  280. }{
  281. {
  282. name: "hidden by policy",
  283. key: AdminConsoleVisibility,
  284. handlerValue: "hide",
  285. wantValue: setting.HiddenByPolicy,
  286. },
  287. {
  288. name: "visibility default",
  289. key: AdminConsoleVisibility,
  290. handlerValue: "show",
  291. wantValue: setting.VisibleByPolicy,
  292. },
  293. {
  294. name: "read non-existing value",
  295. key: AdminConsoleVisibility,
  296. handlerValue: "show",
  297. handlerError: ErrNoSuchKey,
  298. wantValue: setting.VisibleByPolicy,
  299. },
  300. {
  301. name: "other error is returned",
  302. key: AdminConsoleVisibility,
  303. handlerValue: "show",
  304. handlerError: someOtherError,
  305. wantValue: setting.VisibleByPolicy,
  306. wantError: someOtherError,
  307. },
  308. }
  309. for _, tt := range tests {
  310. t.Run(tt.name, func(t *testing.T) {
  311. SetHandlerForTest(t, &testHandler{
  312. t: t,
  313. key: tt.key,
  314. s: tt.handlerValue,
  315. err: tt.handlerError,
  316. })
  317. visibility, err := GetVisibility(tt.key)
  318. if err != tt.wantError {
  319. t.Errorf("err=%q, want %q", err, tt.wantError)
  320. }
  321. if visibility != tt.wantValue {
  322. t.Errorf("visibility=%v, want %v", visibility, tt.wantValue)
  323. }
  324. })
  325. }
  326. }
  327. func TestGetDuration(t *testing.T) {
  328. tests := []struct {
  329. name string
  330. key Key
  331. handlerValue string
  332. handlerError error
  333. defaultValue time.Duration
  334. wantValue time.Duration
  335. wantError error
  336. }{
  337. {
  338. name: "read existing value",
  339. key: KeyExpirationNoticeTime,
  340. handlerValue: "2h",
  341. wantValue: 2 * time.Hour,
  342. defaultValue: 24 * time.Hour,
  343. },
  344. {
  345. name: "invalid duration value",
  346. key: KeyExpirationNoticeTime,
  347. handlerValue: "-20",
  348. wantValue: 24 * time.Hour,
  349. defaultValue: 24 * time.Hour,
  350. },
  351. {
  352. name: "read non-existing value",
  353. key: KeyExpirationNoticeTime,
  354. handlerError: ErrNoSuchKey,
  355. wantValue: 24 * time.Hour,
  356. defaultValue: 24 * time.Hour,
  357. },
  358. {
  359. name: "read non-existing value different default",
  360. key: KeyExpirationNoticeTime,
  361. handlerError: ErrNoSuchKey,
  362. wantValue: 0 * time.Second,
  363. defaultValue: 0 * time.Second,
  364. },
  365. {
  366. name: "other error is returned",
  367. key: KeyExpirationNoticeTime,
  368. handlerError: someOtherError,
  369. wantValue: 24 * time.Hour,
  370. wantError: someOtherError,
  371. defaultValue: 24 * time.Hour,
  372. },
  373. }
  374. for _, tt := range tests {
  375. t.Run(tt.name, func(t *testing.T) {
  376. SetHandlerForTest(t, &testHandler{
  377. t: t,
  378. key: tt.key,
  379. s: tt.handlerValue,
  380. err: tt.handlerError,
  381. })
  382. duration, err := GetDuration(tt.key, tt.defaultValue)
  383. if err != tt.wantError {
  384. t.Errorf("err=%q, want %q", err, tt.wantError)
  385. }
  386. if duration != tt.wantValue {
  387. t.Errorf("duration=%v, want %v", duration, tt.wantValue)
  388. }
  389. })
  390. }
  391. }
  392. func TestGetStringArray(t *testing.T) {
  393. tests := []struct {
  394. name string
  395. key Key
  396. handlerValue []string
  397. handlerError error
  398. defaultValue []string
  399. wantValue []string
  400. wantError error
  401. }{
  402. {
  403. name: "read existing value",
  404. key: AllowedSuggestedExitNodes,
  405. handlerValue: []string{"foo", "bar"},
  406. wantValue: []string{"foo", "bar"},
  407. },
  408. {
  409. name: "read non-existing value",
  410. key: AllowedSuggestedExitNodes,
  411. handlerError: ErrNoSuchKey,
  412. wantError: nil,
  413. },
  414. {
  415. name: "read non-existing value, non nil default",
  416. key: AllowedSuggestedExitNodes,
  417. handlerError: ErrNoSuchKey,
  418. defaultValue: []string{"foo", "bar"},
  419. wantValue: []string{"foo", "bar"},
  420. wantError: nil,
  421. },
  422. {
  423. name: "reading value returns other error",
  424. key: AllowedSuggestedExitNodes,
  425. handlerError: someOtherError,
  426. wantError: someOtherError,
  427. },
  428. }
  429. for _, tt := range tests {
  430. t.Run(tt.name, func(t *testing.T) {
  431. SetHandlerForTest(t, &testHandler{
  432. t: t,
  433. key: tt.key,
  434. sArr: tt.handlerValue,
  435. err: tt.handlerError,
  436. })
  437. value, err := GetStringArray(tt.key, tt.defaultValue)
  438. if err != tt.wantError {
  439. t.Errorf("err=%q, want %q", err, tt.wantError)
  440. }
  441. if !slices.Equal(tt.wantValue, value) {
  442. t.Errorf("value=%v, want %v", value, tt.wantValue)
  443. }
  444. })
  445. }
  446. }
  447. func TestSelectControlURL(t *testing.T) {
  448. tests := []struct {
  449. reg, disk, want string
  450. }{
  451. // Modern default case.
  452. {"", "", "https://controlplane.tailscale.com"},
  453. // For a user who installed prior to Dec 2020, with
  454. // stuff in their registry.
  455. {"https://login.tailscale.com", "", "https://login.tailscale.com"},
  456. // Ignore pre-Dec'20 LoginURL from installer if prefs
  457. // prefs overridden manually to an on-prem control
  458. // server.
  459. {"https://login.tailscale.com", "http://on-prem", "http://on-prem"},
  460. // Something unknown explicitly set in the registry always wins.
  461. {"http://explicit-reg", "", "http://explicit-reg"},
  462. {"http://explicit-reg", "http://on-prem", "http://explicit-reg"},
  463. {"http://explicit-reg", "https://login.tailscale.com", "http://explicit-reg"},
  464. {"http://explicit-reg", "https://controlplane.tailscale.com", "http://explicit-reg"},
  465. // If nothing in the registry, disk wins.
  466. {"", "http://on-prem", "http://on-prem"},
  467. }
  468. for _, tt := range tests {
  469. if got := SelectControlURL(tt.reg, tt.disk); got != tt.want {
  470. t.Errorf("(reg %q, disk %q) = %q; want %q", tt.reg, tt.disk, got, tt.want)
  471. }
  472. }
  473. }