setting_test.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. package setting
  4. import (
  5. "slices"
  6. "strings"
  7. "testing"
  8. "tailscale.com/types/lazy"
  9. "tailscale.com/types/ptr"
  10. "tailscale.com/util/syspolicy/internal"
  11. "tailscale.com/util/syspolicy/pkey"
  12. )
  13. func TestSettingDefinition(t *testing.T) {
  14. tests := []struct {
  15. name string
  16. setting *Definition
  17. osOverride string
  18. wantKey pkey.Key
  19. wantScope Scope
  20. wantType Type
  21. wantIsSupported bool
  22. wantSupportedPlatforms PlatformList
  23. wantString string
  24. }{
  25. {
  26. name: "Nil",
  27. setting: nil,
  28. wantKey: "",
  29. wantScope: 0,
  30. wantType: InvalidValue,
  31. wantIsSupported: false,
  32. wantString: "(nil)",
  33. },
  34. {
  35. name: "Device/Invalid",
  36. setting: NewDefinition("TestDevicePolicySetting", DeviceSetting, InvalidValue),
  37. wantKey: "TestDevicePolicySetting",
  38. wantScope: DeviceSetting,
  39. wantType: InvalidValue,
  40. wantIsSupported: true,
  41. wantString: `Device("TestDevicePolicySetting", Invalid)`,
  42. },
  43. {
  44. name: "Device/Integer",
  45. setting: NewDefinition("TestDevicePolicySetting", DeviceSetting, IntegerValue),
  46. wantKey: "TestDevicePolicySetting",
  47. wantScope: DeviceSetting,
  48. wantType: IntegerValue,
  49. wantIsSupported: true,
  50. wantString: `Device("TestDevicePolicySetting", Integer)`,
  51. },
  52. {
  53. name: "Profile/String",
  54. setting: NewDefinition("TestProfilePolicySetting", ProfileSetting, StringValue),
  55. wantKey: "TestProfilePolicySetting",
  56. wantScope: ProfileSetting,
  57. wantType: StringValue,
  58. wantIsSupported: true,
  59. wantString: `Profile("TestProfilePolicySetting", String)`,
  60. },
  61. {
  62. name: "Device/StringList",
  63. setting: NewDefinition("AllowedSuggestedExitNodes", DeviceSetting, StringListValue),
  64. wantKey: "AllowedSuggestedExitNodes",
  65. wantScope: DeviceSetting,
  66. wantType: StringListValue,
  67. wantIsSupported: true,
  68. wantString: `Device("AllowedSuggestedExitNodes", StringList)`,
  69. },
  70. {
  71. name: "Device/PreferenceOption",
  72. setting: NewDefinition("AdvertiseExitNode", DeviceSetting, PreferenceOptionValue),
  73. wantKey: "AdvertiseExitNode",
  74. wantScope: DeviceSetting,
  75. wantType: PreferenceOptionValue,
  76. wantIsSupported: true,
  77. wantString: `Device("AdvertiseExitNode", PreferenceOption)`,
  78. },
  79. {
  80. name: "User/Boolean",
  81. setting: NewDefinition("TestUserPolicySetting", UserSetting, BooleanValue),
  82. wantKey: "TestUserPolicySetting",
  83. wantScope: UserSetting,
  84. wantType: BooleanValue,
  85. wantIsSupported: true,
  86. wantString: `User("TestUserPolicySetting", Boolean)`,
  87. },
  88. {
  89. name: "User/Visibility",
  90. setting: NewDefinition("AdminConsole", UserSetting, VisibilityValue),
  91. wantKey: "AdminConsole",
  92. wantScope: UserSetting,
  93. wantType: VisibilityValue,
  94. wantIsSupported: true,
  95. wantString: `User("AdminConsole", Visibility)`,
  96. },
  97. {
  98. name: "User/Duration",
  99. setting: NewDefinition("KeyExpirationNotice", UserSetting, DurationValue),
  100. wantKey: "KeyExpirationNotice",
  101. wantScope: UserSetting,
  102. wantType: DurationValue,
  103. wantIsSupported: true,
  104. wantString: `User("KeyExpirationNotice", Duration)`,
  105. },
  106. {
  107. name: "SupportedSetting",
  108. setting: NewDefinition("DesktopPolicySetting", DeviceSetting, StringValue, "macos", "windows"),
  109. osOverride: "windows",
  110. wantKey: "DesktopPolicySetting",
  111. wantScope: DeviceSetting,
  112. wantType: StringValue,
  113. wantIsSupported: true,
  114. wantSupportedPlatforms: PlatformList{"macos", "windows"},
  115. wantString: `Device("DesktopPolicySetting", String)`,
  116. },
  117. {
  118. name: "UnsupportedSetting",
  119. setting: NewDefinition("AndroidPolicySetting", DeviceSetting, StringValue, "android"),
  120. osOverride: "macos",
  121. wantKey: "AndroidPolicySetting",
  122. wantScope: DeviceSetting,
  123. wantType: StringValue,
  124. wantIsSupported: false,
  125. wantSupportedPlatforms: PlatformList{"android"},
  126. wantString: `Device("AndroidPolicySetting", String)`,
  127. },
  128. }
  129. for _, tt := range tests {
  130. t.Run(tt.name, func(t *testing.T) {
  131. if tt.osOverride != "" {
  132. internal.OSForTesting.SetForTest(t, tt.osOverride, nil)
  133. }
  134. if !tt.setting.Equal(tt.setting) {
  135. t.Errorf("the setting should be equal to itself")
  136. }
  137. if tt.setting != nil && !tt.setting.Equal(ptr.To(*tt.setting)) {
  138. t.Errorf("the setting should be equal to its shallow copy")
  139. }
  140. if gotKey := tt.setting.Key(); gotKey != tt.wantKey {
  141. t.Errorf("Key: got %q, want %q", gotKey, tt.wantKey)
  142. }
  143. if gotScope := tt.setting.Scope(); gotScope != tt.wantScope {
  144. t.Errorf("Scope: got %v, want %v", gotScope, tt.wantScope)
  145. }
  146. if gotType := tt.setting.Type(); gotType != tt.wantType {
  147. t.Errorf("Type: got %v, want %v", gotType, tt.wantType)
  148. }
  149. if gotIsSupported := tt.setting.IsSupported(); gotIsSupported != tt.wantIsSupported {
  150. t.Errorf("IsSupported: got %v, want %v", gotIsSupported, tt.wantIsSupported)
  151. }
  152. if gotSupportedPlatforms := tt.setting.SupportedPlatforms(); !slices.Equal(gotSupportedPlatforms, tt.wantSupportedPlatforms) {
  153. t.Errorf("SupportedPlatforms: got %v, want %v", gotSupportedPlatforms, tt.wantSupportedPlatforms)
  154. }
  155. if gotString := tt.setting.String(); gotString != tt.wantString {
  156. t.Errorf("String: got %v, want %v", gotString, tt.wantString)
  157. }
  158. })
  159. }
  160. }
  161. func TestRegisterSettingDefinition(t *testing.T) {
  162. const testPolicySettingKey pkey.Key = "TestPolicySetting"
  163. tests := []struct {
  164. name string
  165. key pkey.Key
  166. wantEq *Definition
  167. wantErr error
  168. }{
  169. {
  170. name: "GetRegistered",
  171. key: "TestPolicySetting",
  172. wantEq: NewDefinition(testPolicySettingKey, DeviceSetting, StringValue),
  173. },
  174. {
  175. name: "GetNonRegistered",
  176. key: "OtherPolicySetting",
  177. wantEq: nil,
  178. wantErr: ErrNoSuchKey,
  179. },
  180. }
  181. resetSettingDefinitions(t)
  182. Register(testPolicySettingKey, DeviceSetting, StringValue)
  183. for _, tt := range tests {
  184. t.Run(tt.name, func(t *testing.T) {
  185. got, gotErr := DefinitionOf(tt.key)
  186. if gotErr != tt.wantErr {
  187. t.Errorf("gotErr %v, wantErr %v", gotErr, tt.wantErr)
  188. }
  189. if !got.Equal(tt.wantEq) {
  190. t.Errorf("got %v, want %v", got, tt.wantEq)
  191. }
  192. })
  193. }
  194. }
  195. func TestRegisterAfterUsePanics(t *testing.T) {
  196. resetSettingDefinitions(t)
  197. Register("TestPolicySetting", DeviceSetting, StringValue)
  198. DefinitionOf("TestPolicySetting")
  199. func() {
  200. defer func() {
  201. if gotPanic, wantPanic := recover(), "policy definitions are already in use"; gotPanic != wantPanic {
  202. t.Errorf("gotPanic: %q, wantPanic: %q", gotPanic, wantPanic)
  203. }
  204. }()
  205. Register("TestPolicySetting", DeviceSetting, StringValue)
  206. }()
  207. }
  208. func TestRegisterDuplicateSettings(t *testing.T) {
  209. tests := []struct {
  210. name string
  211. settings []*Definition
  212. wantEq *Definition
  213. wantErrStr string
  214. }{
  215. {
  216. name: "NoConflict/Exact",
  217. settings: []*Definition{
  218. NewDefinition("TestPolicySetting", DeviceSetting, StringValue),
  219. NewDefinition("TestPolicySetting", DeviceSetting, StringValue),
  220. },
  221. wantEq: NewDefinition("TestPolicySetting", DeviceSetting, StringValue),
  222. },
  223. {
  224. name: "NoConflict/MergeOS-First",
  225. settings: []*Definition{
  226. NewDefinition("TestPolicySetting", DeviceSetting, StringValue, "android", "macos"),
  227. NewDefinition("TestPolicySetting", DeviceSetting, StringValue), // all platforms
  228. },
  229. wantEq: NewDefinition("TestPolicySetting", DeviceSetting, StringValue), // all platforms
  230. },
  231. {
  232. name: "NoConflict/MergeOS-Second",
  233. settings: []*Definition{
  234. NewDefinition("TestPolicySetting", DeviceSetting, StringValue), // all platforms
  235. NewDefinition("TestPolicySetting", DeviceSetting, StringValue, "android", "macos"),
  236. },
  237. wantEq: NewDefinition("TestPolicySetting", DeviceSetting, StringValue), // all platforms
  238. },
  239. {
  240. name: "NoConflict/MergeOS-Both",
  241. settings: []*Definition{
  242. NewDefinition("TestPolicySetting", DeviceSetting, StringValue, "macos"),
  243. NewDefinition("TestPolicySetting", DeviceSetting, StringValue, "windows"),
  244. },
  245. wantEq: NewDefinition("TestPolicySetting", DeviceSetting, StringValue, "macos", "windows"),
  246. },
  247. {
  248. name: "Conflict/Scope",
  249. settings: []*Definition{
  250. NewDefinition("TestPolicySetting", DeviceSetting, StringValue),
  251. NewDefinition("TestPolicySetting", UserSetting, StringValue),
  252. },
  253. wantEq: nil,
  254. wantErrStr: `duplicate policy definition: "TestPolicySetting"`,
  255. },
  256. {
  257. name: "Conflict/Type",
  258. settings: []*Definition{
  259. NewDefinition("TestPolicySetting", UserSetting, StringValue),
  260. NewDefinition("TestPolicySetting", UserSetting, IntegerValue),
  261. },
  262. wantEq: nil,
  263. wantErrStr: `duplicate policy definition: "TestPolicySetting"`,
  264. },
  265. }
  266. for _, tt := range tests {
  267. t.Run(tt.name, func(t *testing.T) {
  268. resetSettingDefinitions(t)
  269. for _, s := range tt.settings {
  270. Register(s.Key(), s.Scope(), s.Type(), s.SupportedPlatforms()...)
  271. }
  272. got, err := DefinitionOf("TestPolicySetting")
  273. var gotErrStr string
  274. if err != nil {
  275. gotErrStr = err.Error()
  276. }
  277. if gotErrStr != tt.wantErrStr {
  278. t.Fatalf("ErrStr: got %q, want %q", gotErrStr, tt.wantErrStr)
  279. }
  280. if !got.Equal(tt.wantEq) {
  281. t.Errorf("Definition got %v, want %v", got, tt.wantEq)
  282. }
  283. if !slices.Equal(got.SupportedPlatforms(), tt.wantEq.SupportedPlatforms()) {
  284. t.Errorf("SupportedPlatforms got %v, want %v", got.SupportedPlatforms(), tt.wantEq.SupportedPlatforms())
  285. }
  286. })
  287. }
  288. }
  289. func TestListSettingDefinitions(t *testing.T) {
  290. definitions := []*Definition{
  291. NewDefinition("TestDevicePolicySetting", DeviceSetting, IntegerValue),
  292. NewDefinition("TestProfilePolicySetting", ProfileSetting, StringValue),
  293. NewDefinition("TestUserPolicySetting", UserSetting, BooleanValue),
  294. NewDefinition("TestStringListPolicySetting", DeviceSetting, StringListValue),
  295. }
  296. if err := SetDefinitionsForTest(t, definitions...); err != nil {
  297. t.Fatalf("SetDefinitionsForTest failed: %v", err)
  298. }
  299. cmp := func(a, b *Definition) int {
  300. return strings.Compare(string(a.Key()), string(b.Key()))
  301. }
  302. want := append([]*Definition{}, definitions...)
  303. slices.SortFunc(want, cmp)
  304. got, err := Definitions()
  305. if err != nil {
  306. t.Fatalf("Definitions failed: %v", err)
  307. }
  308. slices.SortFunc(got, cmp)
  309. if !slices.Equal(got, want) {
  310. t.Errorf("got %v, want %v", got, want)
  311. }
  312. }
  313. func resetSettingDefinitions(t *testing.T) {
  314. t.Cleanup(func() {
  315. definitionsMu.Lock()
  316. definitionsList = nil
  317. definitions = lazy.SyncValue[DefinitionMap]{}
  318. definitionsUsed = false
  319. definitionsMu.Unlock()
  320. })
  321. definitionsMu.Lock()
  322. definitionsList = nil
  323. definitions = lazy.SyncValue[DefinitionMap]{}
  324. definitionsUsed = false
  325. definitionsMu.Unlock()
  326. }