@budibase/backend-core 2.8.29 → 2.8.31-alpha.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 (132) hide show
  1. package/dist/jest.config.js +10 -4
  2. package/dist/jest.config.js.map +1 -1
  3. package/dist/package.json +14 -26
  4. package/dist/src/auth/auth.js.map +1 -1
  5. package/dist/src/cache/appMetadata.d.ts +7 -1
  6. package/dist/src/cache/appMetadata.js +5 -8
  7. package/dist/src/cache/appMetadata.js.map +1 -1
  8. package/dist/src/cache/user.js.map +1 -1
  9. package/dist/src/constants/db.d.ts +1 -39
  10. package/dist/src/constants/db.js +8 -43
  11. package/dist/src/constants/db.js.map +1 -1
  12. package/dist/src/constants/misc.d.ts +2 -0
  13. package/dist/src/constants/misc.js +2 -0
  14. package/dist/src/constants/misc.js.map +1 -1
  15. package/dist/src/db/constants.d.ts +2 -0
  16. package/dist/src/db/constants.js +13 -0
  17. package/dist/src/db/constants.js.map +1 -0
  18. package/dist/src/db/couch/index.d.ts +1 -0
  19. package/dist/src/db/couch/index.js +1 -0
  20. package/dist/src/db/couch/index.js.map +1 -1
  21. package/dist/src/db/searchIndexes/searchIndexes.js.map +1 -1
  22. package/dist/src/db/utils.js +7 -2
  23. package/dist/src/db/utils.js.map +1 -1
  24. package/dist/src/db/views.d.ts +0 -1
  25. package/dist/src/db/views.js +1 -12
  26. package/dist/src/db/views.js.map +1 -1
  27. package/dist/src/environment.d.ts +11 -4
  28. package/dist/src/environment.js +21 -70
  29. package/dist/src/environment.js.map +1 -1
  30. package/dist/src/events/identification.js +3 -3
  31. package/dist/src/events/identification.js.map +1 -1
  32. package/dist/src/logging/index.d.ts +1 -1
  33. package/dist/src/logging/index.js +2 -3
  34. package/dist/src/logging/index.js.map +1 -1
  35. package/dist/src/logging/pino/logger.js +40 -24
  36. package/dist/src/logging/pino/logger.js.map +1 -1
  37. package/dist/src/logging/system.d.ts +9 -0
  38. package/dist/src/logging/system.js +101 -0
  39. package/dist/src/logging/system.js.map +1 -0
  40. package/dist/src/middleware/adminOnly.d.ts +2 -2
  41. package/dist/src/middleware/adminOnly.js +2 -2
  42. package/dist/src/middleware/adminOnly.js.map +1 -1
  43. package/dist/src/middleware/builderOnly.d.ts +2 -2
  44. package/dist/src/middleware/builderOnly.js +16 -2
  45. package/dist/src/middleware/builderOnly.js.map +1 -1
  46. package/dist/src/middleware/builderOrAdmin.d.ts +2 -2
  47. package/dist/src/middleware/builderOrAdmin.js +17 -4
  48. package/dist/src/middleware/builderOrAdmin.js.map +1 -1
  49. package/dist/src/security/permissions.d.ts +31 -18
  50. package/dist/src/security/permissions.js +46 -57
  51. package/dist/src/security/permissions.js.map +1 -1
  52. package/dist/src/security/roles.js +7 -4
  53. package/dist/src/security/roles.js.map +1 -1
  54. package/dist/src/users/db.d.ts +38 -0
  55. package/dist/src/users/db.js +407 -0
  56. package/dist/src/users/db.js.map +1 -0
  57. package/dist/src/users/events.d.ts +5 -0
  58. package/dist/src/users/events.js +169 -0
  59. package/dist/src/users/events.js.map +1 -0
  60. package/dist/src/users/index.d.ts +4 -0
  61. package/dist/src/users/index.js +23 -0
  62. package/dist/src/users/index.js.map +1 -0
  63. package/dist/src/users/lookup.d.ts +13 -0
  64. package/dist/src/users/lookup.js +112 -0
  65. package/dist/src/users/lookup.js.map +1 -0
  66. package/dist/src/{users.d.ts → users/users.d.ts} +4 -2
  67. package/dist/src/{users.js → users/users.js} +24 -4
  68. package/dist/src/users/users.js.map +1 -0
  69. package/dist/src/users/utils.d.ts +14 -0
  70. package/dist/src/users/utils.js +92 -0
  71. package/dist/src/users/utils.js.map +1 -0
  72. package/dist/tests/core/utilities/jestUtils.d.ts +7 -0
  73. package/dist/tests/core/utilities/jestUtils.js +14 -1
  74. package/dist/tests/core/utilities/jestUtils.js.map +1 -1
  75. package/dist/tests/core/utilities/mocks/events.d.ts +0 -1
  76. package/dist/tests/core/utilities/mocks/events.js +0 -1
  77. package/dist/tests/core/utilities/mocks/events.js.map +1 -1
  78. package/dist/tests/core/utilities/mocks/licenses.d.ts +1 -0
  79. package/dist/tests/core/utilities/mocks/licenses.js +8 -4
  80. package/dist/tests/core/utilities/mocks/licenses.js.map +1 -1
  81. package/dist/tests/core/utilities/structures/accounts.js +3 -3
  82. package/dist/tests/core/utilities/structures/accounts.js.map +1 -1
  83. package/dist/tests/core/utilities/structures/scim.js +1 -5
  84. package/dist/tests/core/utilities/structures/scim.js.map +1 -1
  85. package/dist/tests/core/utilities/structures/sso.js +2 -2
  86. package/dist/tests/core/utilities/structures/sso.js.map +1 -1
  87. package/dist/tests/core/utilities/structures/users.d.ts +3 -1
  88. package/dist/tests/core/utilities/structures/users.js +13 -1
  89. package/dist/tests/core/utilities/structures/users.js.map +1 -1
  90. package/dist/tsconfig.build.tsbuildinfo +1 -1
  91. package/jest.config.ts +1 -2
  92. package/package.json +14 -26
  93. package/scripts/test.sh +2 -2
  94. package/src/auth/auth.ts +1 -1
  95. package/src/cache/appMetadata.ts +10 -8
  96. package/src/cache/tests/writethrough.spec.ts +7 -7
  97. package/src/cache/user.ts +1 -1
  98. package/src/constants/db.ts +4 -42
  99. package/src/constants/misc.ts +2 -0
  100. package/src/db/constants.ts +10 -0
  101. package/src/db/couch/index.ts +1 -0
  102. package/src/db/searchIndexes/searchIndexes.ts +1 -1
  103. package/src/db/utils.ts +9 -3
  104. package/src/db/views.ts +0 -11
  105. package/src/environment.ts +24 -4
  106. package/src/events/identification.ts +3 -2
  107. package/src/logging/index.ts +1 -3
  108. package/src/logging/pino/logger.ts +45 -24
  109. package/src/logging/system.ts +81 -0
  110. package/src/logging/tests/system.spec.ts +61 -0
  111. package/src/middleware/adminOnly.ts +4 -6
  112. package/src/middleware/builderOnly.ts +15 -6
  113. package/src/middleware/builderOrAdmin.ts +16 -8
  114. package/src/middleware/tests/builder.spec.ts +180 -0
  115. package/src/security/permissions.ts +5 -21
  116. package/src/security/roles.ts +1 -1
  117. package/src/security/tests/permissions.spec.ts +1 -1
  118. package/src/users/db.ts +460 -0
  119. package/src/users/events.ts +176 -0
  120. package/src/users/index.ts +4 -0
  121. package/src/users/lookup.ts +102 -0
  122. package/src/{users.ts → users/users.ts} +33 -7
  123. package/src/users/utils.ts +55 -0
  124. package/tests/core/utilities/jestUtils.ts +21 -0
  125. package/tests/core/utilities/mocks/events.ts +0 -2
  126. package/tests/core/utilities/mocks/licenses.ts +7 -3
  127. package/tests/core/utilities/structures/accounts.ts +3 -5
  128. package/tests/core/utilities/structures/scim.ts +4 -5
  129. package/tests/core/utilities/structures/sso.ts +2 -2
  130. package/tests/core/utilities/structures/users.ts +19 -0
  131. package/tsconfig.json +2 -2
  132. package/dist/src/users.js.map +0 -1
@@ -0,0 +1,81 @@
1
+ import fs from "fs"
2
+ import path from "path"
3
+ import * as rfs from "rotating-file-stream"
4
+
5
+ import env from "../environment"
6
+ import { budibaseTempDir } from "../objectStore"
7
+
8
+ const logsFileName = `budibase.log`
9
+ const budibaseLogsHistoryFileName = "budibase-logs-history.txt"
10
+
11
+ const logsPath = path.join(budibaseTempDir(), "systemlogs")
12
+
13
+ function getFullPath(fileName: string) {
14
+ return path.join(logsPath, fileName)
15
+ }
16
+
17
+ export function getSingleFileMaxSizeInfo(totalMaxSize: string) {
18
+ const regex = /(\d+)([A-Za-z])/
19
+ const match = totalMaxSize?.match(regex)
20
+ if (!match) {
21
+ console.warn(`totalMaxSize does not have a valid value`, {
22
+ totalMaxSize,
23
+ })
24
+ return undefined
25
+ }
26
+
27
+ const size = +match[1]
28
+ const unit = match[2]
29
+ if (size === 1) {
30
+ switch (unit) {
31
+ case "B":
32
+ return { size: `${size}B`, totalHistoryFiles: 1 }
33
+ case "K":
34
+ return { size: `${(size * 1000) / 2}B`, totalHistoryFiles: 1 }
35
+ case "M":
36
+ return { size: `${(size * 1000) / 2}K`, totalHistoryFiles: 1 }
37
+ case "G":
38
+ return { size: `${(size * 1000) / 2}M`, totalHistoryFiles: 1 }
39
+ default:
40
+ return undefined
41
+ }
42
+ }
43
+
44
+ if (size % 2 === 0) {
45
+ return { size: `${size / 2}${unit}`, totalHistoryFiles: 1 }
46
+ }
47
+
48
+ return { size: `1${unit}`, totalHistoryFiles: size - 1 }
49
+ }
50
+
51
+ export function localFileDestination() {
52
+ const fileInfo = getSingleFileMaxSizeInfo(env.ROLLING_LOG_MAX_SIZE)
53
+ const outFile = rfs.createStream(logsFileName, {
54
+ // As we have a rolling size, we want to half the max size
55
+ size: fileInfo?.size,
56
+ path: logsPath,
57
+ maxFiles: fileInfo?.totalHistoryFiles || 1,
58
+ immutable: true,
59
+ history: budibaseLogsHistoryFileName,
60
+ initialRotation: false,
61
+ })
62
+
63
+ return outFile
64
+ }
65
+
66
+ export function getLogReadStream() {
67
+ const streams = []
68
+ const historyFile = getFullPath(budibaseLogsHistoryFileName)
69
+ if (fs.existsSync(historyFile)) {
70
+ const fileContent = fs.readFileSync(historyFile, "utf-8")
71
+ const historyFiles = fileContent.split("\n")
72
+ for (const historyFile of historyFiles.filter(x => x)) {
73
+ streams.push(fs.readFileSync(historyFile))
74
+ }
75
+ }
76
+
77
+ streams.push(fs.readFileSync(getFullPath(logsFileName)))
78
+
79
+ const combinedContent = Buffer.concat(streams)
80
+ return combinedContent
81
+ }
@@ -0,0 +1,61 @@
1
+ import { getSingleFileMaxSizeInfo } from "../system"
2
+
3
+ describe("system", () => {
4
+ describe("getSingleFileMaxSizeInfo", () => {
5
+ it.each([
6
+ ["100B", "50B"],
7
+ ["200K", "100K"],
8
+ ["20M", "10M"],
9
+ ["4G", "2G"],
10
+ ])(
11
+ "Halving even number (%s) returns halved size and 1 history file (%s)",
12
+ (totalValue, expectedMaxSize) => {
13
+ const result = getSingleFileMaxSizeInfo(totalValue)
14
+ expect(result).toEqual({
15
+ size: expectedMaxSize,
16
+ totalHistoryFiles: 1,
17
+ })
18
+ }
19
+ )
20
+
21
+ it.each([
22
+ ["5B", "1B", 4],
23
+ ["17K", "1K", 16],
24
+ ["21M", "1M", 20],
25
+ ["3G", "1G", 2],
26
+ ])(
27
+ "Halving an odd number (%s) returns as many files as size (-1) (%s)",
28
+ (totalValue, expectedMaxSize, totalHistoryFiles) => {
29
+ const result = getSingleFileMaxSizeInfo(totalValue)
30
+ expect(result).toEqual({
31
+ size: expectedMaxSize,
32
+ totalHistoryFiles,
33
+ })
34
+ }
35
+ )
36
+
37
+ it.each([
38
+ ["1B", "1B"],
39
+ ["1K", "500B"],
40
+ ["1M", "500K"],
41
+ ["1G", "500M"],
42
+ ])(
43
+ "Halving '%s' returns halved unit (%s)",
44
+ (totalValue, expectedMaxSize) => {
45
+ const result = getSingleFileMaxSizeInfo(totalValue)
46
+ expect(result).toEqual({
47
+ size: expectedMaxSize,
48
+ totalHistoryFiles: 1,
49
+ })
50
+ }
51
+ )
52
+
53
+ it.each([[undefined], [""], ["50"], ["wrongvalue"]])(
54
+ "Halving wrongly formatted value ('%s') returns undefined",
55
+ totalValue => {
56
+ const result = getSingleFileMaxSizeInfo(totalValue!)
57
+ expect(result).toBeUndefined()
58
+ }
59
+ )
60
+ })
61
+ })
@@ -1,10 +1,8 @@
1
- import { BBContext } from "@budibase/types"
1
+ import { UserCtx } from "@budibase/types"
2
+ import { isAdmin } from "../users"
2
3
 
3
- export default async (ctx: BBContext, next: any) => {
4
- if (
5
- !ctx.internal &&
6
- (!ctx.user || !ctx.user.admin || !ctx.user.admin.global)
7
- ) {
4
+ export default async (ctx: UserCtx, next: any) => {
5
+ if (!ctx.internal && !isAdmin(ctx.user)) {
8
6
  ctx.throw(403, "Admin user only endpoint.")
9
7
  }
10
8
  return next()
@@ -1,10 +1,19 @@
1
- import { BBContext } from "@budibase/types"
1
+ import { UserCtx } from "@budibase/types"
2
+ import { isBuilder, hasBuilderPermissions } from "../users"
3
+ import { getAppId } from "../context"
4
+ import env from "../environment"
2
5
 
3
- export default async (ctx: BBContext, next: any) => {
4
- if (
5
- !ctx.internal &&
6
- (!ctx.user || !ctx.user.builder || !ctx.user.builder.global)
7
- ) {
6
+ export default async (ctx: UserCtx, next: any) => {
7
+ const appId = getAppId()
8
+ const builderFn = env.isWorker()
9
+ ? hasBuilderPermissions
10
+ : env.isApps()
11
+ ? isBuilder
12
+ : undefined
13
+ if (!builderFn) {
14
+ throw new Error("Service name unknown - middleware inactive.")
15
+ }
16
+ if (!ctx.internal && !builderFn(ctx.user, appId)) {
8
17
  ctx.throw(403, "Builder user only endpoint.")
9
18
  }
10
19
  return next()
@@ -1,12 +1,20 @@
1
- import { BBContext } from "@budibase/types"
1
+ import { UserCtx } from "@budibase/types"
2
+ import { isBuilder, isAdmin, hasBuilderPermissions } from "../users"
3
+ import { getAppId } from "../context"
4
+ import env from "../environment"
2
5
 
3
- export default async (ctx: BBContext, next: any) => {
4
- if (
5
- !ctx.internal &&
6
- (!ctx.user || !ctx.user.builder || !ctx.user.builder.global) &&
7
- (!ctx.user || !ctx.user.admin || !ctx.user.admin.global)
8
- ) {
9
- ctx.throw(403, "Builder user only endpoint.")
6
+ export default async (ctx: UserCtx, next: any) => {
7
+ const appId = getAppId()
8
+ const builderFn = env.isWorker()
9
+ ? hasBuilderPermissions
10
+ : env.isApps()
11
+ ? isBuilder
12
+ : undefined
13
+ if (!builderFn) {
14
+ throw new Error("Service name unknown - middleware inactive.")
15
+ }
16
+ if (!ctx.internal && !builderFn(ctx.user, appId) && !isAdmin(ctx.user)) {
17
+ ctx.throw(403, "Admin/Builder user only endpoint.")
10
18
  }
11
19
  return next()
12
20
  }
@@ -0,0 +1,180 @@
1
+ import adminOnly from "../adminOnly"
2
+ import builderOnly from "../builderOnly"
3
+ import builderOrAdmin from "../builderOrAdmin"
4
+ import { structures } from "../../../tests"
5
+ import { ContextUser, ServiceType } from "@budibase/types"
6
+ import { doInAppContext } from "../../context"
7
+ import env from "../../environment"
8
+ env._set("SERVICE_TYPE", ServiceType.APPS)
9
+
10
+ const appId = "app_aaa"
11
+ const basicUser = structures.users.user()
12
+ const adminUser = structures.users.adminUser()
13
+ const adminOnlyUser = structures.users.adminOnlyUser()
14
+ const builderUser = structures.users.builderUser()
15
+ const appBuilderUser = structures.users.appBuilderUser(appId)
16
+
17
+ function buildUserCtx(user: ContextUser) {
18
+ return {
19
+ internal: false,
20
+ user,
21
+ throw: jest.fn(),
22
+ } as any
23
+ }
24
+
25
+ function passed(throwFn: jest.Func, nextFn: jest.Func) {
26
+ expect(throwFn).not.toBeCalled()
27
+ expect(nextFn).toBeCalled()
28
+ }
29
+
30
+ function threw(throwFn: jest.Func) {
31
+ // cant check next, the throw function doesn't actually throw - so it still continues
32
+ expect(throwFn).toBeCalled()
33
+ }
34
+
35
+ describe("adminOnly middleware", () => {
36
+ it("should allow admin user", () => {
37
+ const ctx = buildUserCtx(adminUser),
38
+ next = jest.fn()
39
+ adminOnly(ctx, next)
40
+ passed(ctx.throw, next)
41
+ })
42
+
43
+ it("should not allow basic user", () => {
44
+ const ctx = buildUserCtx(basicUser),
45
+ next = jest.fn()
46
+ adminOnly(ctx, next)
47
+ threw(ctx.throw)
48
+ })
49
+
50
+ it("should not allow builder user", () => {
51
+ const ctx = buildUserCtx(builderUser),
52
+ next = jest.fn()
53
+ adminOnly(ctx, next)
54
+ threw(ctx.throw)
55
+ })
56
+ })
57
+
58
+ describe("builderOnly middleware", () => {
59
+ it("should allow builder user", () => {
60
+ const ctx = buildUserCtx(builderUser),
61
+ next = jest.fn()
62
+ builderOnly(ctx, next)
63
+ passed(ctx.throw, next)
64
+ })
65
+
66
+ it("should allow app builder user", () => {
67
+ const ctx = buildUserCtx(appBuilderUser),
68
+ next = jest.fn()
69
+ doInAppContext(appId, () => {
70
+ builderOnly(ctx, next)
71
+ })
72
+ passed(ctx.throw, next)
73
+ })
74
+
75
+ it("should allow admin and builder user", () => {
76
+ const ctx = buildUserCtx(adminUser),
77
+ next = jest.fn()
78
+ builderOnly(ctx, next)
79
+ passed(ctx.throw, next)
80
+ })
81
+
82
+ it("should not allow admin user", () => {
83
+ const ctx = buildUserCtx(adminOnlyUser),
84
+ next = jest.fn()
85
+ builderOnly(ctx, next)
86
+ threw(ctx.throw)
87
+ })
88
+
89
+ it("should not allow app builder user to different app", () => {
90
+ const ctx = buildUserCtx(appBuilderUser),
91
+ next = jest.fn()
92
+ doInAppContext("app_bbb", () => {
93
+ builderOnly(ctx, next)
94
+ })
95
+ threw(ctx.throw)
96
+ })
97
+
98
+ it("should not allow basic user", () => {
99
+ const ctx = buildUserCtx(basicUser),
100
+ next = jest.fn()
101
+ builderOnly(ctx, next)
102
+ threw(ctx.throw)
103
+ })
104
+ })
105
+
106
+ describe("builderOrAdmin middleware", () => {
107
+ it("should allow builder user", () => {
108
+ const ctx = buildUserCtx(builderUser),
109
+ next = jest.fn()
110
+ builderOrAdmin(ctx, next)
111
+ passed(ctx.throw, next)
112
+ })
113
+
114
+ it("should allow builder and admin user", () => {
115
+ const ctx = buildUserCtx(adminUser),
116
+ next = jest.fn()
117
+ builderOrAdmin(ctx, next)
118
+ passed(ctx.throw, next)
119
+ })
120
+
121
+ it("should allow admin user", () => {
122
+ const ctx = buildUserCtx(adminOnlyUser),
123
+ next = jest.fn()
124
+ builderOrAdmin(ctx, next)
125
+ passed(ctx.throw, next)
126
+ })
127
+
128
+ it("should allow app builder user", () => {
129
+ const ctx = buildUserCtx(appBuilderUser),
130
+ next = jest.fn()
131
+ doInAppContext(appId, () => {
132
+ builderOrAdmin(ctx, next)
133
+ })
134
+ passed(ctx.throw, next)
135
+ })
136
+
137
+ it("should not allow basic user", () => {
138
+ const ctx = buildUserCtx(basicUser),
139
+ next = jest.fn()
140
+ builderOrAdmin(ctx, next)
141
+ threw(ctx.throw)
142
+ })
143
+ })
144
+
145
+ describe("check service difference", () => {
146
+ it("should not allow without app ID in apps", () => {
147
+ env._set("SERVICE_TYPE", ServiceType.APPS)
148
+ const appId = "app_a"
149
+ const ctx = buildUserCtx({
150
+ ...basicUser,
151
+ builder: {
152
+ apps: [appId],
153
+ },
154
+ })
155
+ const next = jest.fn()
156
+ doInAppContext(appId, () => {
157
+ builderOnly(ctx, next)
158
+ })
159
+ passed(ctx.throw, next)
160
+ doInAppContext("app_b", () => {
161
+ builderOnly(ctx, next)
162
+ })
163
+ threw(ctx.throw)
164
+ })
165
+
166
+ it("should allow without app ID in worker", () => {
167
+ env._set("SERVICE_TYPE", ServiceType.WORKER)
168
+ const ctx = buildUserCtx({
169
+ ...basicUser,
170
+ builder: {
171
+ apps: ["app_a"],
172
+ },
173
+ })
174
+ const next = jest.fn()
175
+ doInAppContext("app_b", () => {
176
+ builderOnly(ctx, next)
177
+ })
178
+ passed(ctx.throw, next)
179
+ })
180
+ })
@@ -1,29 +1,12 @@
1
- const { flatten } = require("lodash")
2
- const { cloneDeep } = require("lodash/fp")
1
+ import { PermissionType, PermissionLevel } from "@budibase/types"
2
+ export { PermissionType, PermissionLevel } from "@budibase/types"
3
+ import flatten from "lodash/flatten"
4
+ import cloneDeep from "lodash/fp/cloneDeep"
3
5
 
4
6
  export type RoleHierarchy = {
5
7
  permissionId: string
6
8
  }[]
7
9
 
8
- export enum PermissionLevel {
9
- READ = "read",
10
- WRITE = "write",
11
- EXECUTE = "execute",
12
- ADMIN = "admin",
13
- }
14
-
15
- // these are the global types, that govern the underlying default behaviour
16
- export enum PermissionType {
17
- APP = "app",
18
- TABLE = "table",
19
- USER = "user",
20
- AUTOMATION = "automation",
21
- WEBHOOK = "webhook",
22
- BUILDER = "builder",
23
- VIEW = "view",
24
- QUERY = "query",
25
- }
26
-
27
10
  export class Permission {
28
11
  type: PermissionType
29
12
  level: PermissionLevel
@@ -173,3 +156,4 @@ export function isPermissionLevelHigherThanRead(level: PermissionLevel) {
173
156
 
174
157
  // utility as a lot of things need simply the builder permission
175
158
  export const BUILDER = PermissionType.BUILDER
159
+ export const GLOBAL_BUILDER = PermissionType.GLOBAL_BUILDER
@@ -3,7 +3,7 @@ import { prefixRoleID, getRoleParams, DocumentType, SEPARATOR } from "../db"
3
3
  import { getAppDB } from "../context"
4
4
  import { doWithDB } from "../db"
5
5
  import { Screen, Role as RoleDoc } from "@budibase/types"
6
- const { cloneDeep } = require("lodash/fp")
6
+ import cloneDeep from "lodash/fp/cloneDeep"
7
7
 
8
8
  export const BUILTIN_ROLE_IDS = {
9
9
  ADMIN: "ADMIN",
@@ -1,4 +1,4 @@
1
- import { cloneDeep } from "lodash"
1
+ import cloneDeep from "lodash/cloneDeep"
2
2
  import * as permissions from "../permissions"
3
3
  import { BUILTIN_ROLE_IDS } from "../roles"
4
4