@northern.tech/store 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (284) hide show
  1. package/.eslintrc.cjs +9 -0
  2. package/.prettierignore +1 -0
  3. package/CHANGELOG.md +12 -0
  4. package/actions.ts +32 -0
  5. package/api/general-api.test.ts +51 -0
  6. package/api/general-api.ts +60 -0
  7. package/api/types/AWSCredentials.ts +16 -0
  8. package/api/types/Actor.ts +35 -0
  9. package/api/types/Addon.ts +17 -0
  10. package/api/types/AddonTenantadm.ts +17 -0
  11. package/api/types/Alert.ts +34 -0
  12. package/api/types/AlertDetails.ts +23 -0
  13. package/api/types/AlertSubject.ts +23 -0
  14. package/api/types/ApiBurst.ts +21 -0
  15. package/api/types/ApiBurstTenantadm.ts +21 -0
  16. package/api/types/ApiLimits.ts +16 -0
  17. package/api/types/ApiLimitsTenantadm.ts +16 -0
  18. package/api/types/ApiQuota.ts +17 -0
  19. package/api/types/ApiQuotaTenantadm.ts +17 -0
  20. package/api/types/Artifact.ts +48 -0
  21. package/api/types/ArtifactDeployments.ts +48 -0
  22. package/api/types/ArtifactInfo.ts +11 -0
  23. package/api/types/ArtifactInfoDeployments.ts +11 -0
  24. package/api/types/ArtifactLink.ts +11 -0
  25. package/api/types/ArtifactTypeInfo.ts +13 -0
  26. package/api/types/ArtifactTypeInfoDeployments.ts +13 -0
  27. package/api/types/ArtifactUpdate.ts +10 -0
  28. package/api/types/ArtifactUploadLink.ts +15 -0
  29. package/api/types/Attribute.ts +36 -0
  30. package/api/types/AttributeInventory.ts +32 -0
  31. package/api/types/AuditLog.ts +26 -0
  32. package/api/types/AuthSet.ts +33 -0
  33. package/api/types/AuthSetDeviceauth.ts +37 -0
  34. package/api/types/AutoAuthRequest.ts +23 -0
  35. package/api/types/AzureSharedAccessSecret.ts +11 -0
  36. package/api/types/BillingInfo.ts +13 -0
  37. package/api/types/BinaryDeltaConfiguration.ts +15 -0
  38. package/api/types/BinaryDeltaConfigurationDeployments.ts +15 -0
  39. package/api/types/BinaryDeltaLimits.ts +15 -0
  40. package/api/types/BinaryDeltaLimitsDeployments.ts +15 -0
  41. package/api/types/BoundingBox.ts +11 -0
  42. package/api/types/CancelRequest.ts +10 -0
  43. package/api/types/CardData.ts +49 -0
  44. package/api/types/CardSetupData.ts +17 -0
  45. package/api/types/CheckoutData.ts +13 -0
  46. package/api/types/Configuration.ts +11 -0
  47. package/api/types/Count.ts +13 -0
  48. package/api/types/Credentials.ts +23 -0
  49. package/api/types/DBusSubsystem.ts +27 -0
  50. package/api/types/DeltaConfiguration.ts +14 -0
  51. package/api/types/DeltaConfigurationDeployments.ts +14 -0
  52. package/api/types/Deployment.ts +95 -0
  53. package/api/types/DeploymentAggregation.ts +18 -0
  54. package/api/types/DeploymentAggregationItem.ts +18 -0
  55. package/api/types/DeploymentAggregationTerm.ts +23 -0
  56. package/api/types/DeploymentAggregationTerms.ts +18 -0
  57. package/api/types/DeploymentAttributeProjection.ts +11 -0
  58. package/api/types/DeploymentAuditlogs.ts +11 -0
  59. package/api/types/DeploymentDeployments.ts +99 -0
  60. package/api/types/DeploymentFilterTerm.ts +37 -0
  61. package/api/types/DeploymentIdentifier.ts +10 -0
  62. package/api/types/DeploymentPhase.ts +23 -0
  63. package/api/types/DeploymentReporting.ts +46 -0
  64. package/api/types/DeploymentSearchTerms.ts +39 -0
  65. package/api/types/DeploymentSortTerm.ts +25 -0
  66. package/api/types/DeploymentStatistics.ts +15 -0
  67. package/api/types/DeploymentStatusStatistics.ts +54 -0
  68. package/api/types/Device.ts +29 -0
  69. package/api/types/DeviceAggregation.ts +18 -0
  70. package/api/types/DeviceAggregationItem.ts +18 -0
  71. package/api/types/DeviceAggregationTerm.ts +27 -0
  72. package/api/types/DeviceAggregationTerms.ts +22 -0
  73. package/api/types/DeviceAttribute.ts +23 -0
  74. package/api/types/DeviceAttributeProjection.ts +15 -0
  75. package/api/types/DeviceAuditlogs.ts +17 -0
  76. package/api/types/DeviceAuthEvent.ts +23 -0
  77. package/api/types/DeviceConfiguration.ts +16 -0
  78. package/api/types/DeviceDeployment.ts +11 -0
  79. package/api/types/DeviceDeployments.ts +31 -0
  80. package/api/types/DeviceDeviceauth.ts +42 -0
  81. package/api/types/DeviceFilterAttribute.ts +22 -0
  82. package/api/types/DeviceFilterTerm.ts +41 -0
  83. package/api/types/DeviceInventory.ts +19 -0
  84. package/api/types/DeviceInventoryInventory.ts +19 -0
  85. package/api/types/DeviceReporting.ts +22 -0
  86. package/api/types/DeviceSearchTerms.ts +39 -0
  87. package/api/types/DeviceSortTerm.ts +29 -0
  88. package/api/types/DeviceState.ts +32 -0
  89. package/api/types/DeviceStateDeviceconnect.ts +33 -0
  90. package/api/types/DeviceStateIot_manager.ts +14 -0
  91. package/api/types/DeviceStatus.ts +19 -0
  92. package/api/types/DeviceWithImage.ts +80 -0
  93. package/api/types/DirectUploadMetadata.ts +18 -0
  94. package/api/types/EmailVerificationCompletion.ts +13 -0
  95. package/api/types/EmailVerificationRequest.ts +13 -0
  96. package/api/types/Error.ts +17 -0
  97. package/api/types/ErrorAuditlogs.ts +13 -0
  98. package/api/types/ErrorDeployments.ts +17 -0
  99. package/api/types/ErrorDeviceauth.ts +13 -0
  100. package/api/types/ErrorDeviceconfig.ts +17 -0
  101. package/api/types/ErrorDeviceconnect.ts +17 -0
  102. package/api/types/ErrorDevicemonitor.ts +17 -0
  103. package/api/types/ErrorExt.ts +18 -0
  104. package/api/types/ErrorInventory.ts +17 -0
  105. package/api/types/ErrorIot_manager.ts +17 -0
  106. package/api/types/ErrorNotFound.ts +17 -0
  107. package/api/types/ErrorReporting.ts +18 -0
  108. package/api/types/ErrorTenantadm.ts +13 -0
  109. package/api/types/ErrorUseradm.ts +17 -0
  110. package/api/types/Event.ts +48 -0
  111. package/api/types/ExternalIdentity.ts +17 -0
  112. package/api/types/Features.ts +17 -0
  113. package/api/types/FileUpload.ts +23 -0
  114. package/api/types/Filter.ts +19 -0
  115. package/api/types/FilterAttribute.ts +21 -0
  116. package/api/types/FilterDefinition.ts +18 -0
  117. package/api/types/FilterDeployments.ts +19 -0
  118. package/api/types/FilterInventory.ts +20 -0
  119. package/api/types/FilterPredicate.ts +78 -0
  120. package/api/types/FilterPredicateDeployments.ts +48 -0
  121. package/api/types/FilterPredicateInventory.ts +76 -0
  122. package/api/types/GeoBoundingBox.ts +10 -0
  123. package/api/types/GeoBoundingBoxFilter.ts +6 -0
  124. package/api/types/GeoDistance.ts +17 -0
  125. package/api/types/GeoDistanceFilter.ts +10 -0
  126. package/api/types/GeoPoint.ts +15 -0
  127. package/api/types/Group.ts +10 -0
  128. package/api/types/HTTP.ts +19 -0
  129. package/api/types/IdentityData.ts +25 -0
  130. package/api/types/InputParameter.ts +8 -0
  131. package/api/types/Integration.ts +24 -0
  132. package/api/types/JobStatus.ts +13 -0
  133. package/api/types/Limit.ts +21 -0
  134. package/api/types/LimitDeployments.ts +21 -0
  135. package/api/types/LimitDeviceauth.ts +10 -0
  136. package/api/types/LimitUseradm.ts +11 -0
  137. package/api/types/Limits.ts +11 -0
  138. package/api/types/LineDescriptor.ts +11 -0
  139. package/api/types/LogSubsystem.ts +23 -0
  140. package/api/types/LoginOptions.ts +18 -0
  141. package/api/types/ManagementAPIConfiguration.ts +5 -0
  142. package/api/types/MenderTypes.ts +183 -0
  143. package/api/types/MonitorConfiguration.ts +22 -0
  144. package/api/types/NewConfigurationDeployment.ts +15 -0
  145. package/api/types/NewConfigurationDeploymentResponse.ts +10 -0
  146. package/api/types/NewDeployment.ts +42 -0
  147. package/api/types/NewDeploymentForGroup.ts +33 -0
  148. package/api/types/NewDeploymentPhase.ts +37 -0
  149. package/api/types/NewDeploymentPhaseDeployments.ts +20 -0
  150. package/api/types/NewDeploymentV2.ts +31 -0
  151. package/api/types/NewTenant.ts +41 -0
  152. package/api/types/Object.ts +35 -0
  153. package/api/types/OwnUserUpdate.ts +21 -0
  154. package/api/types/PasswordResetCompletion.ts +17 -0
  155. package/api/types/PasswordResetRequest.ts +13 -0
  156. package/api/types/Permission.ts +21 -0
  157. package/api/types/PermissionObject.ts +14 -0
  158. package/api/types/PermissionSet.ts +22 -0
  159. package/api/types/PermissionSetWithScope.ts +15 -0
  160. package/api/types/PersonalAccessToken.ts +29 -0
  161. package/api/types/PersonalAccessTokenRequest.ts +18 -0
  162. package/api/types/Plan.ts +19 -0
  163. package/api/types/PlanBindingDetails.ts +13 -0
  164. package/api/types/PlanChangeRequest.ts +14 -0
  165. package/api/types/PlanLimits.ts +12 -0
  166. package/api/types/PreAuthSet.ts +16 -0
  167. package/api/types/Release.ts +32 -0
  168. package/api/types/ReleaseDeployments.ts +32 -0
  169. package/api/types/ReleaseUpdate.ts +13 -0
  170. package/api/types/Releases.ts +9 -0
  171. package/api/types/ReleasesDeleteError.ts +21 -0
  172. package/api/types/ReleasesDeployments.ts +9 -0
  173. package/api/types/Role.ts +19 -0
  174. package/api/types/RolePermission.ts +25 -0
  175. package/api/types/RolePermissionObject.ts +17 -0
  176. package/api/types/RoleUseradm.ts +19 -0
  177. package/api/types/SAMLMetadata.ts +21 -0
  178. package/api/types/Scope.ts +19 -0
  179. package/api/types/SelectAttribute.ts +17 -0
  180. package/api/types/ServiceSubsystem.ts +19 -0
  181. package/api/types/Settings.ts +23 -0
  182. package/api/types/SortCriteria.ts +30 -0
  183. package/api/types/SsoObject.ts +21 -0
  184. package/api/types/Status.ts +17 -0
  185. package/api/types/StatusDeviceauth.ts +18 -0
  186. package/api/types/StatusTenantadm.ts +19 -0
  187. package/api/types/StorageLimit.ts +17 -0
  188. package/api/types/SubscriptionData.ts +8 -0
  189. package/api/types/SupportRequest.ts +17 -0
  190. package/api/types/Tag.ts +27 -0
  191. package/api/types/Tags.ts +10 -0
  192. package/api/types/TaskResult.ts +13 -0
  193. package/api/types/TaskResultCLI.ts +10 -0
  194. package/api/types/TaskResultHTTPRequest.ts +8 -0
  195. package/api/types/TaskResultHTTPResponse.ts +10 -0
  196. package/api/types/Tenant.ts +38 -0
  197. package/api/types/TenantApiLimits.ts +12 -0
  198. package/api/types/TenantApiLimitsTenantadm.ts +12 -0
  199. package/api/types/TenantIdName.ts +14 -0
  200. package/api/types/TenantIdsData.ts +10 -0
  201. package/api/types/TenantInfo.ts +14 -0
  202. package/api/types/TenantShortInfo.ts +21 -0
  203. package/api/types/TenantTenantadm.ts +38 -0
  204. package/api/types/TenantsIdName.ts +6 -0
  205. package/api/types/Update.ts +17 -0
  206. package/api/types/UpdateDeployments.ts +18 -0
  207. package/api/types/UpdateFile.ts +13 -0
  208. package/api/types/UpdateFileDeployments.ts +13 -0
  209. package/api/types/UpdateTypes.ts +8 -0
  210. package/api/types/UpgradeCompleteRequest.ts +23 -0
  211. package/api/types/User.ts +13 -0
  212. package/api/types/UserAuditlogs.ts +13 -0
  213. package/api/types/UserNew.ts +26 -0
  214. package/api/types/UserUpdate.ts +25 -0
  215. package/api/types/UserUseradm.ts +45 -0
  216. package/api/types/UserWithTenantInfo.ts +8 -0
  217. package/api/types/XDeltaArgs.ts +9 -0
  218. package/api/types/XDeltaArgsDeployments.ts +9 -0
  219. package/api/types/XDeltaArgsLimits.ts +10 -0
  220. package/api/types/XDeltaArgsLimitsDeployments.ts +10 -0
  221. package/api/users-api.ts +34 -0
  222. package/appSlice/constants.ts +166 -0
  223. package/appSlice/index.ts +159 -0
  224. package/appSlice/reducer.test.ts +97 -0
  225. package/appSlice/selectors.ts +33 -0
  226. package/appSlice/thunks.test.ts +118 -0
  227. package/appSlice/thunks.ts +148 -0
  228. package/auth.test.ts +36 -0
  229. package/auth.ts +82 -0
  230. package/commonConstants.tsx +252 -0
  231. package/commonSelectors.ts +255 -0
  232. package/constants.ts +22 -0
  233. package/deploymentsSlice/constants.ts +175 -0
  234. package/deploymentsSlice/index.ts +145 -0
  235. package/deploymentsSlice/reducer.test.ts +108 -0
  236. package/deploymentsSlice/selectors.ts +58 -0
  237. package/deploymentsSlice/thunks.test.ts +425 -0
  238. package/deploymentsSlice/thunks.ts +382 -0
  239. package/devicesSlice/constants.ts +56 -0
  240. package/devicesSlice/index.ts +207 -0
  241. package/devicesSlice/reducer.test.ts +194 -0
  242. package/devicesSlice/selectors.ts +127 -0
  243. package/devicesSlice/thunks.test.tsx +1245 -0
  244. package/devicesSlice/thunks.tsx +1098 -0
  245. package/monitorSlice/constants.ts +17 -0
  246. package/monitorSlice/index.ts +65 -0
  247. package/monitorSlice/reducer.test.ts +79 -0
  248. package/monitorSlice/selectors.ts +15 -0
  249. package/monitorSlice/thunks.test.ts +112 -0
  250. package/monitorSlice/thunks.ts +101 -0
  251. package/onboardingSlice/constants.ts +53 -0
  252. package/onboardingSlice/index.ts +60 -0
  253. package/onboardingSlice/reducer.test.ts +45 -0
  254. package/onboardingSlice/selectors.ts +15 -0
  255. package/onboardingSlice/thunks.test.ts +235 -0
  256. package/onboardingSlice/thunks.ts +142 -0
  257. package/organizationSlice/constants.ts +82 -0
  258. package/organizationSlice/index.ts +111 -0
  259. package/organizationSlice/reducer.test.ts +78 -0
  260. package/organizationSlice/selectors.ts +45 -0
  261. package/organizationSlice/thunks.test.ts +619 -0
  262. package/organizationSlice/thunks.ts +383 -0
  263. package/package.json +38 -0
  264. package/releasesSlice/constants.ts +18 -0
  265. package/releasesSlice/index.ts +116 -0
  266. package/releasesSlice/reducer.test.ts +78 -0
  267. package/releasesSlice/selectors.ts +33 -0
  268. package/releasesSlice/thunks.test.ts +389 -0
  269. package/releasesSlice/thunks.ts +379 -0
  270. package/selectors.ts +22 -0
  271. package/store.ts +89 -0
  272. package/storehooks.test.tsx +386 -0
  273. package/storehooks.ts +250 -0
  274. package/thunks.ts +21 -0
  275. package/tsconfig.json +11 -0
  276. package/tsup.config.ts +11 -0
  277. package/usersSlice/constants.ts +374 -0
  278. package/usersSlice/index.ts +151 -0
  279. package/usersSlice/reducer.test.ts +141 -0
  280. package/usersSlice/selectors.ts +60 -0
  281. package/usersSlice/thunks.test.ts +660 -0
  282. package/usersSlice/thunks.ts +773 -0
  283. package/utils.test.ts +187 -0
  284. package/utils.ts +249 -0
@@ -0,0 +1,374 @@
1
+ 'use strict';
2
+
3
+ // Copyright 2015 Northern.tech AS
4
+ //
5
+ // Licensed under the Apache License, Version 2.0 (the "License");
6
+ // you may not use this file except in compliance with the License.
7
+ // You may obtain a copy of the License at
8
+ //
9
+ // http://www.apache.org/licenses/LICENSE-2.0
10
+ //
11
+ // Unless required by applicable law or agreed to in writing, software
12
+ // distributed under the License is distributed on an "AS IS" BASIS,
13
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ // See the License for the specific language governing permissions and
15
+ // limitations under the License.
16
+ import { ALL_DEVICES, ALL_RELEASES, apiUrl, emptyUiPermissions } from '@northern.tech/store/constants';
17
+
18
+ export const useradmApiUrlv1 = `${apiUrl.v1}/useradm`;
19
+ export const useradmApiUrlv2 = `${apiUrl.v2}/useradm`;
20
+ export { useradmApiUrlv1 as useradmApiUrl };
21
+
22
+ const staticRolesByName = {
23
+ admin: 'RBAC_ROLE_PERMIT_ALL',
24
+ readOnly: 'RBAC_ROLE_OBSERVER',
25
+ ci: 'RBAC_ROLE_CI',
26
+ deploymentsManager: 'RBAC_ROLE_DEPLOYMENTS_MANAGER',
27
+ terminalAccess: 'RBAC_ROLE_REMOTE_TERMINAL'
28
+ };
29
+
30
+ export const PermissionTypes = {
31
+ Any: 'any',
32
+ Get: 'GET',
33
+ Post: 'POST',
34
+ Put: 'PUT',
35
+ Delete: 'DELETE',
36
+ Patch: 'PATCH',
37
+ DeviceGroup: 'DEVICE_GROUP',
38
+ DeviceId: 'DEVICE_ID'
39
+ };
40
+
41
+ const permissionSetIds = {
42
+ Basic: 'Basic',
43
+ ConfigureDevices: 'ConfigureDevices',
44
+ ConnectToDevices: 'ConnectToDevices',
45
+ DeployToDevices: 'DeployToDevices',
46
+ ManageDevices: 'ManageDevices',
47
+ ManageReleases: 'ManageReleases',
48
+ ManageUsers: 'ManageUsers',
49
+ ReadAuditLogs: 'ReadAuditLogs',
50
+ ReadDevices: 'ReadDevices',
51
+ ReadReleases: 'ReadReleases',
52
+ ReadUsers: 'ReadUsers',
53
+ SuperUser: 'SuperUser',
54
+ UploadArtifacts: 'UploadArtifacts'
55
+ };
56
+
57
+ export const uiPermissionsById = {
58
+ configure: {
59
+ explanations: { groups: `'Configure' allows the user to use mender-configure features and apply configurations.` },
60
+ permissionLevel: 2,
61
+ permissionSets: { groups: permissionSetIds.ConfigureDevices },
62
+ title: 'Configure',
63
+ value: 'configure',
64
+ verbs: [PermissionTypes.Get, PermissionTypes.Put, PermissionTypes.Post]
65
+ },
66
+ connect: {
67
+ explanations: { groups: `'Connect' allows the user to use mender-connect features and Troubleshoot add-ons.` },
68
+ permissionLevel: 2,
69
+ permissionSets: { groups: permissionSetIds.ConnectToDevices },
70
+ title: 'Connect',
71
+ value: 'connect',
72
+ verbs: [PermissionTypes.Get, PermissionTypes.Put]
73
+ },
74
+ deploy: {
75
+ explanations: { groups: `'Deploy' allows the user to deploy software or configuration updates to devices.` },
76
+ permissionLevel: 2,
77
+ permissionSets: { deployments: permissionSetIds.DeployToDevices, groups: permissionSetIds.DeployToDevices },
78
+ title: 'Deploy',
79
+ value: 'deploy',
80
+ verbs: [PermissionTypes.Post]
81
+ },
82
+ manage: {
83
+ explanations: {
84
+ groups: `'Manage' allows the user to edit device name, notes, and manage authentication status. For 'All devices' it also allows the user to edit and create device groups.`,
85
+ releases: `'Manage' allows the user to upload new artifacts, edit release descriptions and remove artifacts.`
86
+ },
87
+ permissionLevel: 2,
88
+ permissionSets: {
89
+ groups: permissionSetIds.ManageDevices,
90
+ releases: permissionSetIds.ManageReleases,
91
+ userManagement: permissionSetIds.ManageUsers
92
+ },
93
+ title: 'Manage',
94
+ value: 'manage',
95
+ verbs: [PermissionTypes.Post, PermissionTypes.Put, PermissionTypes.Patch]
96
+ },
97
+ read: {
98
+ explanations: { groups: `'Read' allows the user to view devices.` },
99
+ permissionLevel: 1,
100
+ permissionSets: {
101
+ auditlog: permissionSetIds.ReadAuditLogs,
102
+ groups: permissionSetIds.ReadDevices,
103
+ releases: permissionSetIds.ReadReleases,
104
+ userManagement: permissionSetIds.ReadUsers
105
+ },
106
+ title: 'Read',
107
+ value: 'read',
108
+ verbs: [PermissionTypes.Get, PermissionTypes.Post]
109
+ },
110
+ upload: {
111
+ explanations: { groups: `'Upload' allows the user to upload new Artifacts.` },
112
+ unscopedOnly: { releases: true },
113
+ permissionLevel: 1,
114
+ permissionSets: { releases: permissionSetIds.UploadArtifacts },
115
+ title: 'Upload',
116
+ value: 'upload',
117
+ verbs: [PermissionTypes.Post, PermissionTypes.Put, PermissionTypes.Patch]
118
+ }
119
+ };
120
+
121
+ /**
122
+ * _uiPermissions_ represent the possible permissions/ rights that can be given for the area
123
+ * _endpoints_ represent the possible endpoints this definition might be affecting in the UI and what
124
+ * functionality might be affected
125
+ *
126
+ */
127
+ export const scopedPermissionAreas = {
128
+ groups: { key: 'groups', excessiveAccessSelector: ALL_DEVICES, scopeType: 'DeviceGroups' },
129
+ releases: { key: 'releases', excessiveAccessSelector: ALL_RELEASES, scopeType: 'Releases' }
130
+ };
131
+
132
+ export const uiPermissionsByArea = {
133
+ auditlog: {
134
+ endpoints: [{ path: /\/(auditlog)/i, types: [PermissionTypes.Get], uiPermissions: [uiPermissionsById.read] }],
135
+ explanation:
136
+ 'Granting access to the audit log will allow tracing changes to devices, releases and user accounts, as well as providing information about deployments.',
137
+ uiPermissions: [uiPermissionsById.read],
138
+ title: 'System audit log'
139
+ },
140
+ deployments: {
141
+ endpoints: [
142
+ { path: /\/(deployments\/deployments)/i, types: [PermissionTypes.Post, PermissionTypes.Put], uiPermissions: [uiPermissionsById.deploy] },
143
+ { path: /\/(deployments\/deployments)/i, types: [PermissionTypes.Get], uiPermissions: [uiPermissionsById.read] },
144
+ { path: /\/(deployments\/config)/i, types: [PermissionTypes.Get, PermissionTypes.Put], uiPermissions: [uiPermissionsById.manage] }
145
+ ],
146
+ explanation: 'Providing deploy permissions will allow deployments to be created using the releases and devices a user has access to.',
147
+ uiPermissions: [uiPermissionsById.read, uiPermissionsById.deploy],
148
+ title: 'Deployments'
149
+ },
150
+ groups: {
151
+ endpoints: [
152
+ {
153
+ path: /\/(devauth|inventory|deviceconfig|devicemonitor|deviceconnect\/devices)/i,
154
+ types: [PermissionTypes.Get],
155
+ uiPermissions: [uiPermissionsById.read]
156
+ },
157
+ { path: /\/(devauth|inventory)/i, types: [PermissionTypes.Put, PermissionTypes.Post], uiPermissions: [uiPermissionsById.manage] },
158
+ { path: /\/(deviceconfig)/i, types: [PermissionTypes.Get, PermissionTypes.Put, PermissionTypes.Post], uiPermissions: [uiPermissionsById.configure] },
159
+ { path: /\/(deviceconnect\/devices)/i, types: [PermissionTypes.Get, PermissionTypes.Post], uiPermissions: [uiPermissionsById.connect] }
160
+ ],
161
+ explanation: 'Device group management permissions control the degree to which devices in a group can be accessed and moved to other groups.',
162
+ scope: scopedPermissionAreas.groups.scopeType,
163
+ uiPermissions: [uiPermissionsById.read, uiPermissionsById.manage, uiPermissionsById.deploy, uiPermissionsById.configure, uiPermissionsById.connect],
164
+ title: 'Group Management'
165
+ },
166
+ releases: {
167
+ endpoints: [
168
+ { path: /\/(deployments\/artifacts|deployments\/deployments\/releases)/i, types: [PermissionTypes.Get], uiPermissions: [uiPermissionsById.read] },
169
+ {
170
+ path: /\/(deployments\/artifacts|deployments\/deployments\/releases)/i,
171
+ types: [PermissionTypes.Post, PermissionTypes.Put],
172
+ uiPermissions: [uiPermissionsById.read, uiPermissionsById.upload]
173
+ },
174
+ {
175
+ path: /\/(deployments\/artifacts|deployments\/deployments\/releases)/i,
176
+ types: [PermissionTypes.Delete],
177
+ uiPermissions: [uiPermissionsById.read, uiPermissionsById.manage]
178
+ }
179
+ ],
180
+ explanation: 'Release permissions can be granted to allow artifact & release modifications, as well as the creation of new releases.',
181
+ scope: 'ReleaseTags',
182
+ uiPermissions: [uiPermissionsById.read, uiPermissionsById.manage, uiPermissionsById.upload],
183
+ title: 'Release Management'
184
+ },
185
+ userManagement: {
186
+ endpoints: [
187
+ { path: /\/(useradm)/i, types: [PermissionTypes.Get], uiPermissions: [uiPermissionsById.read] },
188
+ { path: /\/(useradm)/i, types: [PermissionTypes.Post], uiPermissions: [uiPermissionsById.manage] }
189
+ ],
190
+ explanation:
191
+ 'User management permissions should be granted carefully, as these allow privilege increases for any users managed by a user with user management permissions',
192
+ uiPermissions: [uiPermissionsById.read, uiPermissionsById.manage],
193
+ title: 'User Management'
194
+ }
195
+ };
196
+
197
+ const permissionMapper = permission => permission.value;
198
+ export const itemUiPermissionsReducer = (accu, { item, uiPermissions }) => (item ? { ...accu, [item]: uiPermissions } : accu);
199
+
200
+ const checkSinglePermission = (permission, requiredPermission) =>
201
+ requiredPermission === permission || uiPermissionsById[permission].permissionLevel > uiPermissionsById[requiredPermission].permissionLevel;
202
+
203
+ export const checkPermissionsObject = (permissions, requiredPermission, scopedAccess, superAccess) =>
204
+ permissions[superAccess]?.some(permission => checkSinglePermission(permission, requiredPermission)) ||
205
+ permissions[scopedAccess]?.some(permission => checkSinglePermission(permission, requiredPermission));
206
+
207
+ export const rolesById = Object.freeze({
208
+ [staticRolesByName.admin]: {
209
+ name: 'Admin',
210
+ value: staticRolesByName.admin,
211
+ description: 'Full access',
212
+ permissions: [], // permissions refers to the values returned from the backend
213
+ uiPermissions: {
214
+ ...emptyUiPermissions,
215
+ auditlog: uiPermissionsByArea.auditlog.uiPermissions.map(permissionMapper),
216
+ deployments: uiPermissionsByArea.deployments.uiPermissions.map(permissionMapper),
217
+ groups: { [ALL_DEVICES]: uiPermissionsByArea.groups.uiPermissions.map(permissionMapper) },
218
+ releases: { [ALL_RELEASES]: uiPermissionsByArea.releases.uiPermissions.map(permissionMapper) },
219
+ userManagement: uiPermissionsByArea.userManagement.uiPermissions.map(permissionMapper)
220
+ }
221
+ },
222
+ [staticRolesByName.readOnly]: {
223
+ name: 'Read Access',
224
+ value: staticRolesByName.readOnly,
225
+ description: '',
226
+ permissions: [],
227
+ uiPermissions: {
228
+ ...emptyUiPermissions,
229
+ deployments: [uiPermissionsById.read.value],
230
+ groups: { [ALL_DEVICES]: [uiPermissionsById.read.value] },
231
+ releases: { [ALL_RELEASES]: [uiPermissionsById.read.value] },
232
+ userManagement: [uiPermissionsById.read.value]
233
+ }
234
+ },
235
+ [staticRolesByName.ci]: {
236
+ name: 'Releases Manager',
237
+ value: staticRolesByName.ci,
238
+ description: '',
239
+ permissions: [],
240
+ uiPermissions: {
241
+ ...emptyUiPermissions,
242
+ releases: { [ALL_RELEASES]: uiPermissionsByArea.releases.uiPermissions.map(permissionMapper) }
243
+ }
244
+ },
245
+ [staticRolesByName.deploymentsManager]: {
246
+ name: 'Deployments Manager',
247
+ value: staticRolesByName.deploymentsManager,
248
+ description: '',
249
+ permissions: [],
250
+ uiPermissions: {
251
+ ...emptyUiPermissions,
252
+ deployments: uiPermissionsByArea.deployments.uiPermissions.map(permissionMapper),
253
+ groups: { [ALL_DEVICES]: [uiPermissionsById.deploy.value, uiPermissionsById.read.value] },
254
+ releases: { [ALL_RELEASES]: [uiPermissionsById.read.value] }
255
+ }
256
+ },
257
+ [staticRolesByName.terminalAccess]: {
258
+ name: 'Troubleshooting',
259
+ value: staticRolesByName.terminalAccess,
260
+ description: 'Access to the troubleshooting features: Remote Terminal, File Transfer, Port Forwarding',
261
+ permissions: [],
262
+ uiPermissions: {
263
+ ...emptyUiPermissions,
264
+ groups: { [ALL_DEVICES]: [uiPermissionsById.connect.value] }
265
+ }
266
+ }
267
+ });
268
+
269
+ export const defaultPermissionSets = {
270
+ [permissionSetIds.Basic]: {
271
+ name: permissionSetIds.Basic,
272
+ result: {
273
+ // this is needed to prevent the detailed permissions of the basic permission set from being interpreted as allowing read & management access to user endpoints
274
+ userManagement: [uiPermissionsById.read.value]
275
+ }
276
+ },
277
+ [permissionSetIds.SuperUser]: {
278
+ name: permissionSetIds.SuperUser,
279
+ result: {
280
+ ...rolesById[staticRolesByName.admin].uiPermissions
281
+ }
282
+ },
283
+ [permissionSetIds.ManageUsers]: {
284
+ name: permissionSetIds.ManageUsers,
285
+ result: {
286
+ userManagement: [uiPermissionsById.manage.value]
287
+ }
288
+ },
289
+ [permissionSetIds.ReadAuditLogs]: {
290
+ name: permissionSetIds.ReadAuditLogs,
291
+ result: {
292
+ auditlog: [uiPermissionsById.read.value]
293
+ }
294
+ },
295
+ [permissionSetIds.ReadReleases]: {
296
+ name: permissionSetIds.ReadReleases,
297
+ result: {
298
+ releases: { [ALL_RELEASES]: [uiPermissionsById.read.value] }
299
+ }
300
+ },
301
+ [permissionSetIds.ReadUsers]: {
302
+ name: permissionSetIds.ReadUsers,
303
+ result: {
304
+ userManagement: [uiPermissionsById.read.value]
305
+ }
306
+ },
307
+ [permissionSetIds.UploadArtifacts]: {
308
+ name: permissionSetIds.UploadArtifacts,
309
+ result: {
310
+ releases: { [ALL_RELEASES]: [uiPermissionsById.upload.value] }
311
+ }
312
+ },
313
+ [permissionSetIds.ManageReleases]: {
314
+ name: permissionSetIds.ManageReleases,
315
+ result: {
316
+ releases: { [ALL_RELEASES]: [uiPermissionsById.manage.value] }
317
+ }
318
+ },
319
+ [permissionSetIds.ConfigureDevices]: {
320
+ name: permissionSetIds.ConfigureDevices,
321
+ result: {
322
+ deployments: [uiPermissionsById.read.value, uiPermissionsById.deploy.value],
323
+ groups: { [ALL_DEVICES]: [uiPermissionsById.read.value, uiPermissionsById.configure.value] }
324
+ }
325
+ },
326
+ [permissionSetIds.ConnectToDevices]: {
327
+ name: permissionSetIds.ConnectToDevices,
328
+ result: {
329
+ groups: { [ALL_DEVICES]: [uiPermissionsById.read.value, uiPermissionsById.connect.value] }
330
+ }
331
+ },
332
+ [permissionSetIds.DeployToDevices]: {
333
+ name: permissionSetIds.DeployToDevices,
334
+ result: {
335
+ deployments: [uiPermissionsById.deploy.value, uiPermissionsById.manage.value, uiPermissionsById.read.value],
336
+ groups: { [ALL_DEVICES]: [uiPermissionsById.read.value, uiPermissionsById.deploy.value] }
337
+ }
338
+ },
339
+ [permissionSetIds.ManageDevices]: {
340
+ name: permissionSetIds.ManageDevices,
341
+ result: {
342
+ groups: { [ALL_DEVICES]: [uiPermissionsById.read.value, uiPermissionsById.manage.value] }
343
+ }
344
+ },
345
+ [permissionSetIds.ReadDevices]: {
346
+ name: permissionSetIds.ReadDevices,
347
+ result: {
348
+ groups: { [ALL_DEVICES]: [uiPermissionsById.read.value] }
349
+ }
350
+ }
351
+ };
352
+
353
+ export const USER_LOGOUT = 'USER_LOGOUT';
354
+
355
+ export const OWN_USER_ID = 'me';
356
+
357
+ export const rolesByName = {
358
+ ...staticRolesByName,
359
+ deploymentCreation: { action: 'CREATE_DEPLOYMENT', object: { type: 'DEVICE_GROUP', value: undefined } },
360
+ groupAccess: { action: 'VIEW_DEVICE', object: { type: 'DEVICE_GROUP', value: undefined } },
361
+ userManagement: { action: 'http', object: { type: 'any', value: `${useradmApiUrlv1}/.*` } }
362
+ };
363
+ export const twoFAStates = {
364
+ enabled: 'enabled',
365
+ disabled: 'disabled',
366
+ unverified: 'unverified'
367
+ };
368
+ export const settingsKeys = { initialized: 'settings-initialized' };
369
+
370
+ export const READ_STATES = {
371
+ read: 'read',
372
+ seen: 'seen',
373
+ unread: 'unread'
374
+ };
@@ -0,0 +1,151 @@
1
+ // Copyright 2023 Northern.tech AS
2
+ //
3
+ // Licensed under the Apache License, Version 2.0 (the "License");
4
+ // you may not use this file except in compliance with the License.
5
+ // You may obtain a copy of the License at
6
+ //
7
+ // http://www.apache.org/licenses/LICENSE-2.0
8
+ //
9
+ // Unless required by applicable law or agreed to in writing, software
10
+ // distributed under the License is distributed on an "AS IS" BASIS,
11
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ // See the License for the specific language governing permissions and
13
+ // limitations under the License.
14
+ // @ts-nocheck
15
+ import { createSlice } from '@reduxjs/toolkit';
16
+
17
+ import { READ_STATES, defaultPermissionSets, rolesById } from './constants';
18
+
19
+ export const sliceName = 'users';
20
+
21
+ export const initialState = {
22
+ activationCode: undefined,
23
+ byId: {},
24
+ currentUser: null,
25
+ currentSession: {
26
+ // { token: window.localStorage.getItem('JWT'), expiresAt: '2023-01-01T00:15:00.000Z' | undefined }, // expiresAt depending on the stay logged in setting
27
+ },
28
+ customColumns: [],
29
+ qrCode: null,
30
+ globalSettings: {
31
+ id_attribute: undefined,
32
+ previousFilters: [],
33
+ previousPhases: [],
34
+ retries: 0
35
+ },
36
+ permissionSetsById: {
37
+ ...defaultPermissionSets
38
+ },
39
+ rolesById: {
40
+ ...rolesById
41
+ },
42
+ settingsInitialized: false,
43
+ showConnectDeviceDialog: false,
44
+ showFeedbackDialog: false,
45
+ showStartupNotification: false,
46
+ tooltips: {
47
+ byId: {
48
+ // <id>: { readState: <read|unread> } // this object is getting enhanced by the tooltip texts in the app constants
49
+ }
50
+ },
51
+ userSettings: {
52
+ columnSelection: [],
53
+ onboarding: {}
54
+ },
55
+ userSettingsInitialized: false
56
+ };
57
+
58
+ export const usersSlice = createSlice({
59
+ name: sliceName,
60
+ initialState,
61
+ reducers: {
62
+ receivedQrCode: (state, action) => {
63
+ state.qrCode = action.payload;
64
+ },
65
+ successfullyLoggedIn: (state, action) => {
66
+ state.currentSession = action.payload;
67
+ },
68
+ receivedUserList: (state, action) => {
69
+ state.byId = action.payload;
70
+ },
71
+ receivedActivationCode: (state, action) => {
72
+ state.activationCode = action.payload;
73
+ },
74
+ receivedUser: (state, action) => {
75
+ state.byId[action.payload.id] = action.payload;
76
+ state.currentUser = action.payload.id;
77
+ },
78
+ createdUser: (state, action) => {
79
+ // the new user gets a 0 as id, since this will be overwritten by the retrieved userlist anyway + there is no way to know the id before
80
+ state.byId[0] = action.payload;
81
+ },
82
+ removedUser: (state, action) => {
83
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
84
+ const { [action.payload]: removedUser, ...byId } = state.byId;
85
+ state.byId = byId;
86
+ state.currentUser = state.currentUser === action.payload ? null : state.currentUser;
87
+ },
88
+ updatedUser: (state, action) => {
89
+ state.byId[action.payload.id] = {
90
+ ...state.byId[action.payload.id],
91
+ ...action.payload
92
+ };
93
+ },
94
+ receivedPermissionSets: (state, action) => {
95
+ state.permissionSetsById = action.payload;
96
+ },
97
+ receivedRoles: (state, action) => {
98
+ state.rolesById = action.payload;
99
+ },
100
+ createdRole: (state, action) => {
101
+ state.rolesById[action.payload.name] = {
102
+ ...state.rolesById[action.payload.name],
103
+ ...action.payload
104
+ };
105
+ },
106
+ removedRole: (state, action) => {
107
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
108
+ const { [action.payload]: toBeRemoved, ...rolesById } = state.rolesById;
109
+ state.rolesById = rolesById;
110
+ },
111
+ setCustomColumns: (state, action) => {
112
+ state.customColumns = action.payload;
113
+ },
114
+ setGlobalSettings: (state, action) => {
115
+ state.settingsInitialized = true;
116
+ state.globalSettings = {
117
+ ...state.globalSettings,
118
+ ...action.payload
119
+ };
120
+ },
121
+ setUserSettings: (state, action) => {
122
+ state.userSettingsInitialized = true;
123
+ state.userSettings = {
124
+ ...state.userSettings,
125
+ ...action.payload
126
+ };
127
+ },
128
+ setTooltipState: (state, action) => {
129
+ const { id, readState = READ_STATES.read } = action.payload;
130
+ state.tooltips.byId[id].readState = readState;
131
+ },
132
+ setTooltipsState: (state, action) => {
133
+ state.tooltips.byId = {
134
+ ...state.tooltips.byId,
135
+ ...action.payload
136
+ };
137
+ },
138
+ setShowFeedbackDialog: (state, action) => {
139
+ state.showFeedbackDialog = action.payload;
140
+ },
141
+ setShowConnectingDialog: (state, action) => {
142
+ state.showConnectDeviceDialog = action.payload;
143
+ },
144
+ setShowStartupNotification: (state, action) => {
145
+ state.showStartupNotification = action.payload;
146
+ }
147
+ }
148
+ });
149
+
150
+ export const actions = usersSlice.actions;
151
+ export default usersSlice.reducer;
@@ -0,0 +1,141 @@
1
+ // Copyright 2020 Northern.tech AS
2
+ //
3
+ // Licensed under the Apache License, Version 2.0 (the "License");
4
+ // you may not use this file except in compliance with the License.
5
+ // You may obtain a copy of the License at
6
+ //
7
+ // http://www.apache.org/licenses/LICENSE-2.0
8
+ //
9
+ // Unless required by applicable law or agreed to in writing, software
10
+ // distributed under the License is distributed on an "AS IS" BASIS,
11
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ // See the License for the specific language governing permissions and
13
+ // limitations under the License.
14
+ // @ts-nocheck
15
+ import reducer, { actions, initialState } from '.';
16
+ import { defaultState } from '../../../../tests/mockData';
17
+
18
+ const testUser = {
19
+ created_ts: '',
20
+ email: 'test@example.com',
21
+ id: '123',
22
+ roles: ['RBAC_ROLE_PERMIT_ALL'],
23
+ tfasecret: '',
24
+ updated_ts: ''
25
+ };
26
+
27
+ const newDescription = 'new description';
28
+
29
+ describe('user reducer', () => {
30
+ it('should return the initial state', async () => {
31
+ expect(reducer(undefined, {})).toEqual(initialState);
32
+ });
33
+
34
+ it('should handle RECEIVED_QR_CODE', async () => {
35
+ expect(reducer(undefined, { type: actions.receivedQrCode, payload: '123' }).qrCode).toEqual('123');
36
+ expect(reducer(initialState, { type: actions.receivedQrCode, payload: '123' }).qrCode).toEqual('123');
37
+ });
38
+
39
+ it('should handle SUCCESSFULLY_LOGGED_IN', async () => {
40
+ expect(reducer(undefined, { type: actions.successfullyLoggedIn, payload: '123' }).currentSession).toEqual('123');
41
+ expect(reducer(initialState, { type: actions.successfullyLoggedIn, payload: '123' }).currentSession).toEqual('123');
42
+ });
43
+
44
+ it('should handle RECEIVED_USER_LIST', async () => {
45
+ expect(reducer(undefined, { type: actions.receivedUserList, payload: { '123': testUser } }).byId).toEqual({ '123': testUser });
46
+ expect(reducer({ ...initialState, byId: { '123': testUser } }, { type: actions.receivedUserList, payload: { '456': testUser } }).byId).toEqual({
47
+ '456': testUser
48
+ });
49
+ });
50
+
51
+ it('should handle RECEIVED_ACTIVATION_CODE', async () => {
52
+ expect(reducer(undefined, { type: actions.receivedActivationCode, payload: 'code' }).activationCode).toEqual('code');
53
+ expect(reducer({ ...initialState }, { type: actions.receivedActivationCode, payload: 'code' }).activationCode).toEqual('code');
54
+ });
55
+
56
+ it('should handle RECEIVED_USER', async () => {
57
+ expect(reducer(undefined, { type: actions.receivedUser, payload: testUser }).byId).toEqual({ '123': testUser });
58
+ expect(reducer({ ...initialState, byId: { '123': testUser } }, { type: actions.receivedUser, payload: testUser }).byId).toEqual({ '123': testUser });
59
+ });
60
+
61
+ it('should handle CREATED_USER', async () => {
62
+ expect(reducer(undefined, { type: actions.createdUser, payload: testUser }).byId).toEqual({ 0: testUser });
63
+ expect(reducer({ ...initialState, byId: { '123': testUser } }, { type: actions.createdUser, payload: testUser }).byId).toEqual({
64
+ '123': testUser,
65
+ 0: testUser
66
+ });
67
+ });
68
+
69
+ it('should handle REMOVED_USER', async () => {
70
+ expect(reducer(undefined, { type: actions.removedUser, payload: '123' }).byId).toEqual({});
71
+ expect(reducer({ ...initialState, byId: { '123': testUser, '456': testUser } }, { type: actions.removedUser, payload: '123' }).byId).toEqual({
72
+ '456': testUser
73
+ });
74
+ });
75
+
76
+ it('should handle UPDATED_USER', async () => {
77
+ expect(reducer(undefined, { type: actions.updatedUser, payload: testUser }).byId).toEqual({ '123': testUser });
78
+
79
+ expect(
80
+ reducer({ ...initialState, byId: { '123': testUser } }, { type: actions.updatedUser, payload: { ...testUser, email: 'test@mender.io' } }).byId['123']
81
+ .email
82
+ ).toEqual('test@mender.io');
83
+ });
84
+ it('should handle RECEIVED_ROLES', async () => {
85
+ const roles = reducer(undefined, { type: actions.receivedRoles, payload: { ...defaultState.users.rolesById } }).rolesById;
86
+ Object.entries(defaultState.users.rolesById).forEach(([key, role]) => expect(roles[key]).toEqual(role));
87
+ expect(
88
+ reducer(
89
+ { ...initialState, rolesById: { ...defaultState.users.rolesById, thingsRole: { test: 'test' } } },
90
+ { type: actions.receivedRoles, payload: { ...defaultState.users.rolesById } }
91
+ ).rolesById.thingsRole
92
+ ).toBeFalsy();
93
+ });
94
+ it('should handle REMOVED_ROLE', async () => {
95
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
96
+ const { [defaultState.users.rolesById.test.name]: removedRole, ...rolesById } = defaultState.users.rolesById;
97
+ expect(reducer(undefined, { type: actions.removedRole, payload: defaultState.users.rolesById.test.name }).rolesById.test).toBeFalsy();
98
+ expect(
99
+ reducer(
100
+ { ...initialState, rolesById: { ...defaultState.users.rolesById } },
101
+ { type: actions.removedRole, payload: defaultState.users.rolesById.test.name }
102
+ ).rolesById.test
103
+ ).toBeFalsy();
104
+ });
105
+ it('should handle CREATED_ROLE', async () => {
106
+ expect(
107
+ reducer(undefined, { type: actions.createdRole, payload: { name: 'newRole', description: newDescription, groups: ['123'] } }).rolesById.newRole
108
+ .description
109
+ ).toEqual(newDescription);
110
+ expect(
111
+ reducer({ ...initialState }, { type: actions.createdRole, payload: { name: 'newRole', description: newDescription, groups: ['123'] } }).rolesById.newRole
112
+ .description
113
+ ).toEqual(newDescription);
114
+ });
115
+ it('should handle UPDATED_ROLE', async () => {
116
+ expect(
117
+ reducer(undefined, { type: actions.createdRole, payload: { name: 'RBAC_ROLE_CI', description: newDescription } }).rolesById.RBAC_ROLE_CI.name
118
+ ).toEqual('RBAC_ROLE_CI');
119
+ expect(
120
+ reducer({ ...initialState }, { type: actions.createdRole, payload: { name: 'RBAC_ROLE_CI', description: newDescription } }).rolesById.RBAC_ROLE_CI.name
121
+ ).toEqual('RBAC_ROLE_CI');
122
+ });
123
+ it('should handle SET_CUSTOM_COLUMNS', async () => {
124
+ expect(reducer(undefined, { type: actions.setCustomColumns, payload: 'test' }).customColumns).toEqual('test');
125
+ expect(reducer({ ...initialState }, { type: actions.setCustomColumns, payload: 'test' }).customColumns).toEqual('test');
126
+ });
127
+ it('should handle SET_GLOBAL_SETTINGS', async () => {
128
+ expect(reducer(undefined, { type: actions.setGlobalSettings, payload: { newSetting: 'test' } }).globalSettings).toEqual({
129
+ ...initialState.globalSettings,
130
+ newSetting: 'test'
131
+ });
132
+ expect(reducer({ ...initialState }, { type: actions.setGlobalSettings, payload: { newSetting: 'test' } }).globalSettings).toEqual({
133
+ ...initialState.globalSettings,
134
+ newSetting: 'test'
135
+ });
136
+ });
137
+ it('should handle SET_SHOW_CONNECT_DEVICE', async () => {
138
+ expect(reducer(undefined, { type: actions.setShowConnectingDialog, payload: false }).showConnectDeviceDialog).toEqual(false);
139
+ expect(reducer({ ...initialState }, { type: actions.setShowConnectingDialog, payload: true }).showConnectDeviceDialog).toEqual(true);
140
+ });
141
+ });