setting_test.go 11 KB

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