policy_scope_test.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. package setting
  4. import (
  5. "reflect"
  6. "testing"
  7. jsonv2 "github.com/go-json-experiment/json"
  8. )
  9. func TestPolicyScopeIsApplicableSetting(t *testing.T) {
  10. tests := []struct {
  11. name string
  12. scope PolicyScope
  13. setting *Definition
  14. wantApplicable bool
  15. }{
  16. {
  17. name: "DeviceScope/DeviceSetting",
  18. scope: DeviceScope,
  19. setting: NewDefinition("TestSetting", DeviceSetting, IntegerValue),
  20. wantApplicable: true,
  21. },
  22. {
  23. name: "DeviceScope/ProfileSetting",
  24. scope: DeviceScope,
  25. setting: NewDefinition("TestSetting", ProfileSetting, IntegerValue),
  26. wantApplicable: false,
  27. },
  28. {
  29. name: "DeviceScope/UserSetting",
  30. scope: DeviceScope,
  31. setting: NewDefinition("TestSetting", UserSetting, IntegerValue),
  32. wantApplicable: false,
  33. },
  34. {
  35. name: "ProfileScope/DeviceSetting",
  36. scope: CurrentProfileScope,
  37. setting: NewDefinition("TestSetting", DeviceSetting, IntegerValue),
  38. wantApplicable: true,
  39. },
  40. {
  41. name: "ProfileScope/ProfileSetting",
  42. scope: CurrentProfileScope,
  43. setting: NewDefinition("TestSetting", ProfileSetting, IntegerValue),
  44. wantApplicable: true,
  45. },
  46. {
  47. name: "ProfileScope/UserSetting",
  48. scope: CurrentProfileScope,
  49. setting: NewDefinition("TestSetting", UserSetting, IntegerValue),
  50. wantApplicable: false,
  51. },
  52. {
  53. name: "UserScope/DeviceSetting",
  54. scope: CurrentUserScope,
  55. setting: NewDefinition("TestSetting", DeviceSetting, IntegerValue),
  56. wantApplicable: true,
  57. },
  58. {
  59. name: "UserScope/ProfileSetting",
  60. scope: CurrentUserScope,
  61. setting: NewDefinition("TestSetting", ProfileSetting, IntegerValue),
  62. wantApplicable: true,
  63. },
  64. {
  65. name: "UserScope/UserSetting",
  66. scope: CurrentUserScope,
  67. setting: NewDefinition("TestSetting", UserSetting, IntegerValue),
  68. wantApplicable: true,
  69. },
  70. }
  71. for _, tt := range tests {
  72. t.Run(tt.name, func(t *testing.T) {
  73. gotApplicable := tt.scope.IsApplicableSetting(tt.setting)
  74. if gotApplicable != tt.wantApplicable {
  75. t.Fatalf("got %v, want %v", gotApplicable, tt.wantApplicable)
  76. }
  77. })
  78. }
  79. }
  80. func TestPolicyScopeIsConfigurableSetting(t *testing.T) {
  81. tests := []struct {
  82. name string
  83. scope PolicyScope
  84. setting *Definition
  85. wantConfigurable bool
  86. }{
  87. {
  88. name: "DeviceScope/DeviceSetting",
  89. scope: DeviceScope,
  90. setting: NewDefinition("TestSetting", DeviceSetting, IntegerValue),
  91. wantConfigurable: true,
  92. },
  93. {
  94. name: "DeviceScope/ProfileSetting",
  95. scope: DeviceScope,
  96. setting: NewDefinition("TestSetting", ProfileSetting, IntegerValue),
  97. wantConfigurable: true,
  98. },
  99. {
  100. name: "DeviceScope/UserSetting",
  101. scope: DeviceScope,
  102. setting: NewDefinition("TestSetting", UserSetting, IntegerValue),
  103. wantConfigurable: true,
  104. },
  105. {
  106. name: "ProfileScope/DeviceSetting",
  107. scope: CurrentProfileScope,
  108. setting: NewDefinition("TestSetting", DeviceSetting, IntegerValue),
  109. wantConfigurable: false,
  110. },
  111. {
  112. name: "ProfileScope/ProfileSetting",
  113. scope: CurrentProfileScope,
  114. setting: NewDefinition("TestSetting", ProfileSetting, IntegerValue),
  115. wantConfigurable: true,
  116. },
  117. {
  118. name: "ProfileScope/UserSetting",
  119. scope: CurrentProfileScope,
  120. setting: NewDefinition("TestSetting", UserSetting, IntegerValue),
  121. wantConfigurable: true,
  122. },
  123. {
  124. name: "UserScope/DeviceSetting",
  125. scope: CurrentUserScope,
  126. setting: NewDefinition("TestSetting", DeviceSetting, IntegerValue),
  127. wantConfigurable: false,
  128. },
  129. {
  130. name: "UserScope/ProfileSetting",
  131. scope: CurrentUserScope,
  132. setting: NewDefinition("TestSetting", ProfileSetting, IntegerValue),
  133. wantConfigurable: false,
  134. },
  135. {
  136. name: "UserScope/UserSetting",
  137. scope: CurrentUserScope,
  138. setting: NewDefinition("TestSetting", UserSetting, IntegerValue),
  139. wantConfigurable: true,
  140. },
  141. }
  142. for _, tt := range tests {
  143. t.Run(tt.name, func(t *testing.T) {
  144. gotConfigurable := tt.scope.IsConfigurableSetting(tt.setting)
  145. if gotConfigurable != tt.wantConfigurable {
  146. t.Fatalf("got %v, want %v", gotConfigurable, tt.wantConfigurable)
  147. }
  148. })
  149. }
  150. }
  151. func TestPolicyScopeContains(t *testing.T) {
  152. tests := []struct {
  153. name string
  154. scopeA PolicyScope
  155. scopeB PolicyScope
  156. wantAContainsB bool
  157. wantAStrictlyContainsB bool
  158. }{
  159. {
  160. name: "DeviceScope/DeviceScope",
  161. scopeA: DeviceScope,
  162. scopeB: DeviceScope,
  163. wantAContainsB: true,
  164. wantAStrictlyContainsB: false,
  165. },
  166. {
  167. name: "DeviceScope/CurrentProfileScope",
  168. scopeA: DeviceScope,
  169. scopeB: CurrentProfileScope,
  170. wantAContainsB: true,
  171. wantAStrictlyContainsB: true,
  172. },
  173. {
  174. name: "DeviceScope/UserScope",
  175. scopeA: DeviceScope,
  176. scopeB: CurrentUserScope,
  177. wantAContainsB: true,
  178. wantAStrictlyContainsB: true,
  179. },
  180. {
  181. name: "ProfileScope/DeviceScope",
  182. scopeA: CurrentProfileScope,
  183. scopeB: DeviceScope,
  184. wantAContainsB: false,
  185. wantAStrictlyContainsB: false,
  186. },
  187. {
  188. name: "ProfileScope/ProfileScope",
  189. scopeA: CurrentProfileScope,
  190. scopeB: CurrentProfileScope,
  191. wantAContainsB: true,
  192. wantAStrictlyContainsB: false,
  193. },
  194. {
  195. name: "ProfileScope/UserScope",
  196. scopeA: CurrentProfileScope,
  197. scopeB: CurrentUserScope,
  198. wantAContainsB: true,
  199. wantAStrictlyContainsB: true,
  200. },
  201. {
  202. name: "UserScope/DeviceScope",
  203. scopeA: CurrentUserScope,
  204. scopeB: DeviceScope,
  205. wantAContainsB: false,
  206. wantAStrictlyContainsB: false,
  207. },
  208. {
  209. name: "UserScope/ProfileScope",
  210. scopeA: CurrentUserScope,
  211. scopeB: CurrentProfileScope,
  212. wantAContainsB: false,
  213. wantAStrictlyContainsB: false,
  214. },
  215. {
  216. name: "UserScope/UserScope",
  217. scopeA: CurrentUserScope,
  218. scopeB: CurrentUserScope,
  219. wantAContainsB: true,
  220. wantAStrictlyContainsB: false,
  221. },
  222. {
  223. name: "UserScope(1234)/UserScope(1234)",
  224. scopeA: UserScopeOf("1234"),
  225. scopeB: UserScopeOf("1234"),
  226. wantAContainsB: true,
  227. wantAStrictlyContainsB: false,
  228. },
  229. {
  230. name: "UserScope(1234)/UserScope(5678)",
  231. scopeA: UserScopeOf("1234"),
  232. scopeB: UserScopeOf("5678"),
  233. wantAContainsB: false,
  234. wantAStrictlyContainsB: false,
  235. },
  236. {
  237. name: "ProfileScope(A)/UserScope(A/1234)",
  238. scopeA: PolicyScope{kind: ProfileSetting, profileID: "A"},
  239. scopeB: PolicyScope{kind: UserSetting, userID: "1234", profileID: "A"},
  240. wantAContainsB: true,
  241. wantAStrictlyContainsB: true,
  242. },
  243. {
  244. name: "ProfileScope(A)/UserScope(B/1234)",
  245. scopeA: PolicyScope{kind: ProfileSetting, profileID: "A"},
  246. scopeB: PolicyScope{kind: UserSetting, userID: "1234", profileID: "B"},
  247. wantAContainsB: false,
  248. wantAStrictlyContainsB: false,
  249. },
  250. {
  251. name: "UserScope(1234)/UserScope(A/1234)",
  252. scopeA: PolicyScope{kind: UserSetting, userID: "1234"},
  253. scopeB: PolicyScope{kind: UserSetting, userID: "1234", profileID: "A"},
  254. wantAContainsB: true,
  255. wantAStrictlyContainsB: true,
  256. },
  257. {
  258. name: "UserScope(1234)/UserScope(A/5678)",
  259. scopeA: PolicyScope{kind: UserSetting, userID: "1234"},
  260. scopeB: PolicyScope{kind: UserSetting, userID: "5678", profileID: "A"},
  261. wantAContainsB: false,
  262. wantAStrictlyContainsB: false,
  263. },
  264. }
  265. for _, tt := range tests {
  266. t.Run(tt.name, func(t *testing.T) {
  267. gotContains := tt.scopeA.Contains(tt.scopeB)
  268. if gotContains != tt.wantAContainsB {
  269. t.Fatalf("WithinOf: got %v, want %v", gotContains, tt.wantAContainsB)
  270. }
  271. gotStrictlyContains := tt.scopeA.StrictlyContains(tt.scopeB)
  272. if gotStrictlyContains != tt.wantAStrictlyContainsB {
  273. t.Fatalf("StrictlyWithinOf: got %v, want %v", gotStrictlyContains, tt.wantAStrictlyContainsB)
  274. }
  275. })
  276. }
  277. }
  278. func TestPolicyScopeMarshalUnmarshal(t *testing.T) {
  279. tests := []struct {
  280. name string
  281. in any
  282. wantJSON string
  283. wantError bool
  284. }{
  285. {
  286. name: "null-scope",
  287. in: &struct {
  288. Scope PolicyScope
  289. }{},
  290. wantJSON: `{"Scope":"Device"}`,
  291. },
  292. {
  293. name: "null-scope-omit-zero",
  294. in: &struct {
  295. Scope PolicyScope `json:",omitzero"`
  296. }{},
  297. wantJSON: `{}`,
  298. },
  299. {
  300. name: "device-scope",
  301. in: &struct {
  302. Scope PolicyScope
  303. }{DeviceScope},
  304. wantJSON: `{"Scope":"Device"}`,
  305. },
  306. {
  307. name: "current-profile-scope",
  308. in: &struct {
  309. Scope PolicyScope
  310. }{CurrentProfileScope},
  311. wantJSON: `{"Scope":"Profile"}`,
  312. },
  313. {
  314. name: "current-user-scope",
  315. in: &struct {
  316. Scope PolicyScope
  317. }{CurrentUserScope},
  318. wantJSON: `{"Scope":"User"}`,
  319. },
  320. {
  321. name: "specific-user-scope",
  322. in: &struct {
  323. Scope PolicyScope
  324. }{UserScopeOf("_")},
  325. wantJSON: `{"Scope":"User(_)"}`,
  326. },
  327. {
  328. name: "specific-user-scope",
  329. in: &struct {
  330. Scope PolicyScope
  331. }{UserScopeOf("S-1-5-21-3698941153-1525015703-2649197413-1001")},
  332. wantJSON: `{"Scope":"User(S-1-5-21-3698941153-1525015703-2649197413-1001)"}`,
  333. },
  334. {
  335. name: "specific-profile-scope",
  336. in: &struct {
  337. Scope PolicyScope
  338. }{PolicyScope{kind: ProfileSetting, profileID: "1234"}},
  339. wantJSON: `{"Scope":"Profile(1234)"}`,
  340. },
  341. {
  342. name: "specific-profile-and-user-scope",
  343. in: &struct {
  344. Scope PolicyScope
  345. }{PolicyScope{
  346. kind: UserSetting,
  347. profileID: "1234",
  348. userID: "S-1-5-21-3698941153-1525015703-2649197413-1001",
  349. }},
  350. wantJSON: `{"Scope":"Profile(1234)/User(S-1-5-21-3698941153-1525015703-2649197413-1001)"}`,
  351. },
  352. }
  353. for _, tt := range tests {
  354. t.Run(tt.name, func(t *testing.T) {
  355. gotJSON, err := jsonv2.Marshal(tt.in)
  356. if err != nil {
  357. t.Fatalf("Marshal failed: %v", err)
  358. }
  359. if string(gotJSON) != tt.wantJSON {
  360. t.Fatalf("Marshal got %s, want %s", gotJSON, tt.wantJSON)
  361. }
  362. wantBack := tt.in
  363. gotBack := reflect.New(reflect.TypeOf(tt.in).Elem()).Interface()
  364. err = jsonv2.Unmarshal(gotJSON, gotBack)
  365. if err != nil {
  366. t.Fatalf("Unmarshal failed: %v", err)
  367. }
  368. if !reflect.DeepEqual(gotBack, wantBack) {
  369. t.Fatalf("Unmarshal got %+v, want %+v", gotBack, wantBack)
  370. }
  371. })
  372. }
  373. }
  374. func TestPolicyScopeUnmarshalSpecial(t *testing.T) {
  375. tests := []struct {
  376. name string
  377. json string
  378. want any
  379. wantError bool
  380. }{
  381. {
  382. name: "empty",
  383. json: "{}",
  384. want: &struct {
  385. Scope PolicyScope
  386. }{},
  387. },
  388. {
  389. name: "too-many-scopes",
  390. json: `{"Scope":"Device/Profile/User"}`,
  391. wantError: true,
  392. },
  393. {
  394. name: "user/profile", // incorrect order
  395. json: `{"Scope":"User/Profile"}`,
  396. wantError: true,
  397. },
  398. {
  399. name: "profile-user-no-params",
  400. json: `{"Scope":"Profile/User"}`,
  401. want: &struct {
  402. Scope PolicyScope
  403. }{CurrentUserScope},
  404. },
  405. {
  406. name: "unknown-scope",
  407. json: `{"Scope":"Unknown"}`,
  408. wantError: true,
  409. },
  410. {
  411. name: "unknown-scope/unknown-scope",
  412. json: `{"Scope":"Unknown/Unknown"}`,
  413. wantError: true,
  414. },
  415. {
  416. name: "device-scope/unknown-scope",
  417. json: `{"Scope":"Device/Unknown"}`,
  418. wantError: true,
  419. },
  420. {
  421. name: "unknown-scope/device-scope",
  422. json: `{"Scope":"Unknown/Device"}`,
  423. wantError: true,
  424. },
  425. {
  426. name: "slash",
  427. json: `{"Scope":"/"}`,
  428. wantError: true,
  429. },
  430. {
  431. name: "empty",
  432. json: `{"Scope": ""`,
  433. wantError: true,
  434. },
  435. {
  436. name: "no-closing-bracket",
  437. json: `{"Scope": "user(1234"`,
  438. wantError: true,
  439. },
  440. {
  441. name: "device-with-id",
  442. json: `{"Scope": "device(123)"`,
  443. wantError: true,
  444. },
  445. }
  446. for _, tt := range tests {
  447. t.Run(tt.name, func(t *testing.T) {
  448. got := &struct {
  449. Scope PolicyScope
  450. }{}
  451. err := jsonv2.Unmarshal([]byte(tt.json), got)
  452. if (err != nil) != tt.wantError {
  453. t.Errorf("Marshal error: got %v, want %v", err, tt.wantError)
  454. }
  455. if err != nil {
  456. return
  457. }
  458. if !reflect.DeepEqual(got, tt.want) {
  459. t.Fatalf("Unmarshal got %+v, want %+v", got, tt.want)
  460. }
  461. })
  462. }
  463. }
  464. func TestExtractScopeAndParams(t *testing.T) {
  465. tests := []struct {
  466. name string
  467. s string
  468. scope string
  469. params string
  470. wantOk bool
  471. }{
  472. {
  473. name: "empty",
  474. s: "",
  475. wantOk: true,
  476. },
  477. {
  478. name: "scope-only",
  479. s: "device",
  480. scope: "device",
  481. wantOk: true,
  482. },
  483. {
  484. name: "scope-with-params",
  485. s: "user(1234)",
  486. scope: "user",
  487. params: "1234",
  488. wantOk: true,
  489. },
  490. {
  491. name: "params-empty-scope",
  492. s: "(1234)",
  493. scope: "",
  494. params: "1234",
  495. wantOk: true,
  496. },
  497. {
  498. name: "params-with-brackets",
  499. s: "test()())))())",
  500. scope: "test",
  501. params: ")())))()",
  502. wantOk: true,
  503. },
  504. {
  505. name: "no-closing-bracket",
  506. s: "user(1234",
  507. scope: "",
  508. params: "",
  509. wantOk: false,
  510. },
  511. {
  512. name: "open-before-close",
  513. s: ")user(1234",
  514. scope: "",
  515. params: "",
  516. wantOk: false,
  517. },
  518. {
  519. name: "brackets-only",
  520. s: ")(",
  521. scope: "",
  522. params: "",
  523. wantOk: false,
  524. },
  525. {
  526. name: "closing-bracket",
  527. s: ")",
  528. scope: "",
  529. params: "",
  530. wantOk: false,
  531. },
  532. {
  533. name: "opening-bracket",
  534. s: ")",
  535. scope: "",
  536. params: "",
  537. wantOk: false,
  538. },
  539. }
  540. for _, tt := range tests {
  541. t.Run(tt.name, func(t *testing.T) {
  542. scope, params, ok := extractScopeAndParams(tt.s)
  543. if ok != tt.wantOk {
  544. t.Logf("OK: got %v; want %v", ok, tt.wantOk)
  545. }
  546. if scope != tt.scope {
  547. t.Logf("Scope: got %q; want %q", scope, tt.scope)
  548. }
  549. if params != tt.params {
  550. t.Logf("Params: got %v; want %v", params, tt.params)
  551. }
  552. })
  553. }
  554. }