@budibase/backend-core 2.21.2 → 2.21.4
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.
- package/dist/index.js +68 -57
- package/dist/index.js.map +3 -3
- package/dist/index.js.meta.json +1 -1
- package/dist/package.json +6 -5
- package/dist/plugins.js.meta.json +1 -1
- package/dist/src/cache/base/index.d.ts +1 -1
- package/dist/src/cache/generic.d.ts +1 -1
- package/dist/src/cache/user.js.map +1 -1
- package/dist/src/db/Replication.d.ts +13 -25
- package/dist/src/db/Replication.js +18 -33
- package/dist/src/db/Replication.js.map +1 -1
- package/dist/src/redis/redis.d.ts +9 -5
- package/dist/src/redis/redis.js +46 -3
- package/dist/src/redis/redis.js.map +1 -1
- package/dist/src/redis/redlockImpl.js.map +1 -1
- package/dist/src/security/roles.d.ts +9 -10
- package/dist/src/security/roles.js +15 -18
- package/dist/src/security/roles.js.map +1 -1
- package/dist/tests/core/utilities/structures/accounts.js +1 -1
- package/dist/tests/core/utilities/structures/accounts.js.map +1 -1
- package/dist/tests/core/utilities/structures/scim.js +1 -1
- package/dist/tests/core/utilities/structures/scim.js.map +1 -1
- package/package.json +6 -5
- package/src/cache/user.ts +2 -2
- package/src/db/Replication.ts +27 -40
- package/src/redis/redis.ts +58 -9
- package/src/redis/redlockImpl.ts +1 -1
- package/src/redis/tests/redis.spec.ts +214 -0
- package/src/security/roles.ts +27 -35
- package/tests/core/utilities/structures/accounts.ts +1 -1
- package/tests/core/utilities/structures/scim.ts +1 -1
package/src/redis/redis.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import env from "../environment"
|
|
2
|
-
import Redis from "ioredis"
|
|
2
|
+
import Redis, { Cluster } from "ioredis"
|
|
3
3
|
// mock-redis doesn't have any typing
|
|
4
4
|
let MockRedis: any | undefined
|
|
5
5
|
if (env.MOCK_REDIS) {
|
|
@@ -28,7 +28,7 @@ const DEFAULT_SELECT_DB = SelectableDatabase.DEFAULT
|
|
|
28
28
|
|
|
29
29
|
// for testing just generate the client once
|
|
30
30
|
let CLOSED = false
|
|
31
|
-
|
|
31
|
+
const CLIENTS: Record<number, Redis> = {}
|
|
32
32
|
let CONNECTED = false
|
|
33
33
|
|
|
34
34
|
// mock redis always connected
|
|
@@ -36,7 +36,7 @@ if (env.MOCK_REDIS) {
|
|
|
36
36
|
CONNECTED = true
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
function pickClient(selectDb: number)
|
|
39
|
+
function pickClient(selectDb: number) {
|
|
40
40
|
return CLIENTS[selectDb]
|
|
41
41
|
}
|
|
42
42
|
|
|
@@ -201,12 +201,15 @@ class RedisWrapper {
|
|
|
201
201
|
key = `${db}${SEPARATOR}${key}`
|
|
202
202
|
let stream
|
|
203
203
|
if (CLUSTERED) {
|
|
204
|
-
let node = this.getClient().nodes("master")
|
|
204
|
+
let node = (this.getClient() as never as Cluster).nodes("master")
|
|
205
205
|
stream = node[0].scanStream({ match: key + "*", count: 100 })
|
|
206
206
|
} else {
|
|
207
|
-
stream = this.getClient().scanStream({
|
|
207
|
+
stream = (this.getClient() as Redis).scanStream({
|
|
208
|
+
match: key + "*",
|
|
209
|
+
count: 100,
|
|
210
|
+
})
|
|
208
211
|
}
|
|
209
|
-
return promisifyStream(stream, this.getClient())
|
|
212
|
+
return promisifyStream(stream, this.getClient() as any)
|
|
210
213
|
}
|
|
211
214
|
|
|
212
215
|
async keys(pattern: string) {
|
|
@@ -221,14 +224,16 @@ class RedisWrapper {
|
|
|
221
224
|
|
|
222
225
|
async get(key: string) {
|
|
223
226
|
const db = this._db
|
|
224
|
-
|
|
227
|
+
const response = await this.getClient().get(addDbPrefix(db, key))
|
|
225
228
|
// overwrite the prefixed key
|
|
229
|
+
// @ts-ignore
|
|
226
230
|
if (response != null && response.key) {
|
|
231
|
+
// @ts-ignore
|
|
227
232
|
response.key = key
|
|
228
233
|
}
|
|
229
234
|
// if its not an object just return the response
|
|
230
235
|
try {
|
|
231
|
-
return JSON.parse(response)
|
|
236
|
+
return JSON.parse(response!)
|
|
232
237
|
} catch (err) {
|
|
233
238
|
return response
|
|
234
239
|
}
|
|
@@ -274,13 +279,37 @@ class RedisWrapper {
|
|
|
274
279
|
}
|
|
275
280
|
}
|
|
276
281
|
|
|
282
|
+
async bulkStore(
|
|
283
|
+
data: Record<string, any>,
|
|
284
|
+
expirySeconds: number | null = null
|
|
285
|
+
) {
|
|
286
|
+
const client = this.getClient()
|
|
287
|
+
|
|
288
|
+
const dataToStore = Object.entries(data).reduce((acc, [key, value]) => {
|
|
289
|
+
acc[addDbPrefix(this._db, key)] =
|
|
290
|
+
typeof value === "object" ? JSON.stringify(value) : value
|
|
291
|
+
return acc
|
|
292
|
+
}, {} as Record<string, any>)
|
|
293
|
+
|
|
294
|
+
const pipeline = client.pipeline()
|
|
295
|
+
pipeline.mset(dataToStore)
|
|
296
|
+
|
|
297
|
+
if (expirySeconds !== null) {
|
|
298
|
+
for (const key of Object.keys(dataToStore)) {
|
|
299
|
+
pipeline.expire(key, expirySeconds)
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
await pipeline.exec()
|
|
304
|
+
}
|
|
305
|
+
|
|
277
306
|
async getTTL(key: string) {
|
|
278
307
|
const db = this._db
|
|
279
308
|
const prefixedKey = addDbPrefix(db, key)
|
|
280
309
|
return this.getClient().ttl(prefixedKey)
|
|
281
310
|
}
|
|
282
311
|
|
|
283
|
-
async setExpiry(key: string, expirySeconds: number
|
|
312
|
+
async setExpiry(key: string, expirySeconds: number) {
|
|
284
313
|
const db = this._db
|
|
285
314
|
const prefixedKey = addDbPrefix(db, key)
|
|
286
315
|
await this.getClient().expire(prefixedKey, expirySeconds)
|
|
@@ -295,6 +324,26 @@ class RedisWrapper {
|
|
|
295
324
|
let items = await this.scan()
|
|
296
325
|
await Promise.all(items.map((obj: any) => this.delete(obj.key)))
|
|
297
326
|
}
|
|
327
|
+
|
|
328
|
+
async increment(key: string) {
|
|
329
|
+
const result = await this.getClient().incr(addDbPrefix(this._db, key))
|
|
330
|
+
if (isNaN(result)) {
|
|
331
|
+
throw new Error(`Redis ${key} does not contain a number`)
|
|
332
|
+
}
|
|
333
|
+
return result
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
async deleteIfValue(key: string, value: any) {
|
|
337
|
+
const client = this.getClient()
|
|
338
|
+
|
|
339
|
+
const luaScript = `
|
|
340
|
+
if redis.call('GET', KEYS[1]) == ARGV[1] then
|
|
341
|
+
redis.call('DEL', KEYS[1])
|
|
342
|
+
end
|
|
343
|
+
`
|
|
344
|
+
|
|
345
|
+
await client.eval(luaScript, 1, addDbPrefix(this._db, key), value)
|
|
346
|
+
}
|
|
298
347
|
}
|
|
299
348
|
|
|
300
349
|
export default RedisWrapper
|
package/src/redis/redlockImpl.ts
CHANGED
|
@@ -72,7 +72,7 @@ const OPTIONS: Record<keyof typeof LockType, Redlock.Options> = {
|
|
|
72
72
|
export async function newRedlock(opts: Redlock.Options = {}) {
|
|
73
73
|
const options = { ...OPTIONS.DEFAULT, ...opts }
|
|
74
74
|
const redisWrapper = await getLockClient()
|
|
75
|
-
const client = redisWrapper.getClient()
|
|
75
|
+
const client = redisWrapper.getClient() as any
|
|
76
76
|
return new Redlock([client], options)
|
|
77
77
|
}
|
|
78
78
|
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { GenericContainer, StartedTestContainer } from "testcontainers"
|
|
2
|
+
import { generator, structures } from "../../../tests"
|
|
3
|
+
import RedisWrapper from "../redis"
|
|
4
|
+
import { env } from "../.."
|
|
5
|
+
|
|
6
|
+
jest.setTimeout(30000)
|
|
7
|
+
|
|
8
|
+
describe("redis", () => {
|
|
9
|
+
let redis: RedisWrapper
|
|
10
|
+
let container: StartedTestContainer
|
|
11
|
+
|
|
12
|
+
beforeAll(async () => {
|
|
13
|
+
const container = await new GenericContainer("redis")
|
|
14
|
+
.withExposedPorts(6379)
|
|
15
|
+
.start()
|
|
16
|
+
|
|
17
|
+
env._set(
|
|
18
|
+
"REDIS_URL",
|
|
19
|
+
`${container.getHost()}:${container.getMappedPort(6379)}`
|
|
20
|
+
)
|
|
21
|
+
env._set("MOCK_REDIS", 0)
|
|
22
|
+
env._set("REDIS_PASSWORD", 0)
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
afterAll(() => container?.stop())
|
|
26
|
+
|
|
27
|
+
beforeEach(async () => {
|
|
28
|
+
redis = new RedisWrapper(structures.db.id())
|
|
29
|
+
await redis.init()
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
describe("store", () => {
|
|
33
|
+
it("a basic value can be persisted", async () => {
|
|
34
|
+
const key = structures.uuid()
|
|
35
|
+
const value = generator.word()
|
|
36
|
+
|
|
37
|
+
await redis.store(key, value)
|
|
38
|
+
|
|
39
|
+
expect(await redis.get(key)).toEqual(value)
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
it("objects can be persisted", async () => {
|
|
43
|
+
const key = structures.uuid()
|
|
44
|
+
const value = { [generator.word()]: generator.word() }
|
|
45
|
+
|
|
46
|
+
await redis.store(key, value)
|
|
47
|
+
|
|
48
|
+
expect(await redis.get(key)).toEqual(value)
|
|
49
|
+
})
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
describe("bulkStore", () => {
|
|
53
|
+
function createRandomObject(
|
|
54
|
+
keyLength: number,
|
|
55
|
+
valueGenerator: () => any = () => generator.word()
|
|
56
|
+
) {
|
|
57
|
+
return generator
|
|
58
|
+
.unique(() => generator.word(), keyLength)
|
|
59
|
+
.reduce((acc, key) => {
|
|
60
|
+
acc[key] = valueGenerator()
|
|
61
|
+
return acc
|
|
62
|
+
}, {} as Record<string, string>)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
it("a basic object can be persisted", async () => {
|
|
66
|
+
const data = createRandomObject(10)
|
|
67
|
+
|
|
68
|
+
await redis.bulkStore(data)
|
|
69
|
+
|
|
70
|
+
for (const [key, value] of Object.entries(data)) {
|
|
71
|
+
expect(await redis.get(key)).toEqual(value)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
expect(await redis.keys("*")).toHaveLength(10)
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
it("a complex object can be persisted", async () => {
|
|
78
|
+
const data = {
|
|
79
|
+
...createRandomObject(10, () => createRandomObject(5)),
|
|
80
|
+
...createRandomObject(5),
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
await redis.bulkStore(data)
|
|
84
|
+
|
|
85
|
+
for (const [key, value] of Object.entries(data)) {
|
|
86
|
+
expect(await redis.get(key)).toEqual(value)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
expect(await redis.keys("*")).toHaveLength(15)
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
it("no TTL is set by default", async () => {
|
|
93
|
+
const data = createRandomObject(10)
|
|
94
|
+
|
|
95
|
+
await redis.bulkStore(data)
|
|
96
|
+
|
|
97
|
+
for (const [key, value] of Object.entries(data)) {
|
|
98
|
+
expect(await redis.get(key)).toEqual(value)
|
|
99
|
+
expect(await redis.getTTL(key)).toEqual(-1)
|
|
100
|
+
}
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
it("a bulk store can be persisted with TTL", async () => {
|
|
104
|
+
const ttl = 500
|
|
105
|
+
const data = createRandomObject(8)
|
|
106
|
+
|
|
107
|
+
await redis.bulkStore(data, ttl)
|
|
108
|
+
|
|
109
|
+
for (const [key, value] of Object.entries(data)) {
|
|
110
|
+
expect(await redis.get(key)).toEqual(value)
|
|
111
|
+
expect(await redis.getTTL(key)).toEqual(ttl)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
expect(await redis.keys("*")).toHaveLength(8)
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
it("setting a TTL of -1 will not persist the key", async () => {
|
|
118
|
+
const ttl = -1
|
|
119
|
+
const data = createRandomObject(5)
|
|
120
|
+
|
|
121
|
+
await redis.bulkStore(data, ttl)
|
|
122
|
+
|
|
123
|
+
for (const [key, value] of Object.entries(data)) {
|
|
124
|
+
expect(await redis.get(key)).toBe(null)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
expect(await redis.keys("*")).toHaveLength(0)
|
|
128
|
+
})
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
describe("increment", () => {
|
|
132
|
+
it("can increment on a new key", async () => {
|
|
133
|
+
const key = structures.uuid()
|
|
134
|
+
const result = await redis.increment(key)
|
|
135
|
+
expect(result).toBe(1)
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
it("can increment multiple times", async () => {
|
|
139
|
+
const key = structures.uuid()
|
|
140
|
+
const results = [
|
|
141
|
+
await redis.increment(key),
|
|
142
|
+
await redis.increment(key),
|
|
143
|
+
await redis.increment(key),
|
|
144
|
+
await redis.increment(key),
|
|
145
|
+
await redis.increment(key),
|
|
146
|
+
]
|
|
147
|
+
expect(results).toEqual([1, 2, 3, 4, 5])
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
it("can increment on a new key", async () => {
|
|
151
|
+
const key1 = structures.uuid()
|
|
152
|
+
const key2 = structures.uuid()
|
|
153
|
+
|
|
154
|
+
const result1 = await redis.increment(key1)
|
|
155
|
+
expect(result1).toBe(1)
|
|
156
|
+
|
|
157
|
+
const result2 = await redis.increment(key2)
|
|
158
|
+
expect(result2).toBe(1)
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
it("can increment multiple times in parallel", async () => {
|
|
162
|
+
const key = structures.uuid()
|
|
163
|
+
const results = await Promise.all(
|
|
164
|
+
Array.from({ length: 100 }).map(() => redis.increment(key))
|
|
165
|
+
)
|
|
166
|
+
expect(results).toHaveLength(100)
|
|
167
|
+
expect(results).toEqual(Array.from({ length: 100 }).map((_, i) => i + 1))
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
it("can increment existing set keys", async () => {
|
|
171
|
+
const key = structures.uuid()
|
|
172
|
+
await redis.store(key, 70)
|
|
173
|
+
await redis.increment(key)
|
|
174
|
+
|
|
175
|
+
const result = await redis.increment(key)
|
|
176
|
+
expect(result).toBe(72)
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
it.each([
|
|
180
|
+
generator.word(),
|
|
181
|
+
generator.bool(),
|
|
182
|
+
{ [generator.word()]: generator.word() },
|
|
183
|
+
])("cannot increment if the store value is not a number", async value => {
|
|
184
|
+
const key = structures.uuid()
|
|
185
|
+
await redis.store(key, value)
|
|
186
|
+
|
|
187
|
+
await expect(redis.increment(key)).rejects.toThrowError(
|
|
188
|
+
"ERR value is not an integer or out of range"
|
|
189
|
+
)
|
|
190
|
+
})
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
describe("deleteIfValue", () => {
|
|
194
|
+
it("can delete if the value matches", async () => {
|
|
195
|
+
const key = structures.uuid()
|
|
196
|
+
const value = generator.word()
|
|
197
|
+
await redis.store(key, value)
|
|
198
|
+
|
|
199
|
+
await redis.deleteIfValue(key, value)
|
|
200
|
+
|
|
201
|
+
expect(await redis.get(key)).toBeNull()
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
it("will not delete if the value does not matches", async () => {
|
|
205
|
+
const key = structures.uuid()
|
|
206
|
+
const value = generator.word()
|
|
207
|
+
await redis.store(key, value)
|
|
208
|
+
|
|
209
|
+
await redis.deleteIfValue(key, generator.word())
|
|
210
|
+
|
|
211
|
+
expect(await redis.get(key)).toEqual(value)
|
|
212
|
+
})
|
|
213
|
+
})
|
|
214
|
+
})
|
package/src/security/roles.ts
CHANGED
|
@@ -84,25 +84,24 @@ export function getBuiltinRoles(): { [key: string]: RoleDoc } {
|
|
|
84
84
|
return cloneDeep(BUILTIN_ROLES)
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
export
|
|
88
|
-
role
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
export const BUILTIN_ROLE_NAME_ARRAY = Object.values(BUILTIN_ROLES).map(
|
|
92
|
-
role => role.name
|
|
93
|
-
)
|
|
87
|
+
export function isBuiltin(role: string) {
|
|
88
|
+
return getBuiltinRole(role) !== undefined
|
|
89
|
+
}
|
|
94
90
|
|
|
95
|
-
export function
|
|
96
|
-
|
|
91
|
+
export function getBuiltinRole(roleId: string): Role | undefined {
|
|
92
|
+
const role = Object.values(BUILTIN_ROLES).find(role =>
|
|
93
|
+
roleId.includes(role._id)
|
|
94
|
+
)
|
|
95
|
+
if (!role) {
|
|
96
|
+
return undefined
|
|
97
|
+
}
|
|
98
|
+
return cloneDeep(role)
|
|
97
99
|
}
|
|
98
100
|
|
|
99
101
|
/**
|
|
100
102
|
* Works through the inheritance ranks to see how far up the builtin stack this ID is.
|
|
101
103
|
*/
|
|
102
|
-
export function builtinRoleToNumber(id
|
|
103
|
-
if (!id) {
|
|
104
|
-
return 0
|
|
105
|
-
}
|
|
104
|
+
export function builtinRoleToNumber(id: string) {
|
|
106
105
|
const builtins = getBuiltinRoles()
|
|
107
106
|
const MAX = Object.values(builtins).length + 1
|
|
108
107
|
if (id === BUILTIN_IDS.ADMIN || id === BUILTIN_IDS.BUILDER) {
|
|
@@ -123,7 +122,7 @@ export function builtinRoleToNumber(id?: string) {
|
|
|
123
122
|
/**
|
|
124
123
|
* Converts any role to a number, but has to be async to get the roles from db.
|
|
125
124
|
*/
|
|
126
|
-
export async function roleToNumber(id
|
|
125
|
+
export async function roleToNumber(id: string) {
|
|
127
126
|
if (isBuiltin(id)) {
|
|
128
127
|
return builtinRoleToNumber(id)
|
|
129
128
|
}
|
|
@@ -131,7 +130,7 @@ export async function roleToNumber(id?: string) {
|
|
|
131
130
|
defaultPublic: true,
|
|
132
131
|
})) as RoleDoc[]
|
|
133
132
|
for (let role of hierarchy) {
|
|
134
|
-
if (isBuiltin(role
|
|
133
|
+
if (role?.inherits && isBuiltin(role.inherits)) {
|
|
135
134
|
return builtinRoleToNumber(role.inherits) + 1
|
|
136
135
|
}
|
|
137
136
|
}
|
|
@@ -161,35 +160,28 @@ export function lowerBuiltinRoleID(roleId1?: string, roleId2?: string): string {
|
|
|
161
160
|
* @returns The role object, which may contain an "inherits" property.
|
|
162
161
|
*/
|
|
163
162
|
export async function getRole(
|
|
164
|
-
roleId
|
|
163
|
+
roleId: string,
|
|
165
164
|
opts?: { defaultPublic?: boolean }
|
|
166
|
-
): Promise<RoleDoc
|
|
167
|
-
if (!roleId) {
|
|
168
|
-
return undefined
|
|
169
|
-
}
|
|
170
|
-
let role: any = {}
|
|
165
|
+
): Promise<RoleDoc> {
|
|
171
166
|
// built in roles mostly come from the in-code implementation,
|
|
172
167
|
// but can be extended by a doc stored about them (e.g. permissions)
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
Object.values(BUILTIN_ROLES).find(role => role._id === roleId)
|
|
176
|
-
)
|
|
177
|
-
} else {
|
|
168
|
+
let role: RoleDoc | undefined = getBuiltinRole(roleId)
|
|
169
|
+
if (!role) {
|
|
178
170
|
// make sure has the prefix (if it has it then it won't be added)
|
|
179
171
|
roleId = prefixRoleID(roleId)
|
|
180
172
|
}
|
|
181
173
|
try {
|
|
182
174
|
const db = getAppDB()
|
|
183
|
-
const dbRole = await db.get(getDBRoleID(roleId))
|
|
184
|
-
role = Object.assign(role, dbRole)
|
|
175
|
+
const dbRole = await db.get<RoleDoc>(getDBRoleID(roleId))
|
|
176
|
+
role = Object.assign(role || {}, dbRole)
|
|
185
177
|
// finalise the ID
|
|
186
|
-
role._id = getExternalRoleID(role._id
|
|
178
|
+
role._id = getExternalRoleID(role._id!, role.version)
|
|
187
179
|
} catch (err) {
|
|
188
180
|
if (!isBuiltin(roleId) && opts?.defaultPublic) {
|
|
189
181
|
return cloneDeep(BUILTIN_ROLES.PUBLIC)
|
|
190
182
|
}
|
|
191
183
|
// only throw an error if there is no role at all
|
|
192
|
-
if (Object.keys(role).length === 0) {
|
|
184
|
+
if (!role || Object.keys(role).length === 0) {
|
|
193
185
|
throw err
|
|
194
186
|
}
|
|
195
187
|
}
|
|
@@ -200,7 +192,7 @@ export async function getRole(
|
|
|
200
192
|
* Simple function to get all the roles based on the top level user role ID.
|
|
201
193
|
*/
|
|
202
194
|
async function getAllUserRoles(
|
|
203
|
-
userRoleId
|
|
195
|
+
userRoleId: string,
|
|
204
196
|
opts?: { defaultPublic?: boolean }
|
|
205
197
|
): Promise<RoleDoc[]> {
|
|
206
198
|
// admins have access to all roles
|
|
@@ -226,7 +218,7 @@ async function getAllUserRoles(
|
|
|
226
218
|
}
|
|
227
219
|
|
|
228
220
|
export async function getUserRoleIdHierarchy(
|
|
229
|
-
userRoleId
|
|
221
|
+
userRoleId: string
|
|
230
222
|
): Promise<string[]> {
|
|
231
223
|
const roles = await getUserRoleHierarchy(userRoleId)
|
|
232
224
|
return roles.map(role => role._id!)
|
|
@@ -241,7 +233,7 @@ export async function getUserRoleIdHierarchy(
|
|
|
241
233
|
* highest level of access and the last being the lowest level.
|
|
242
234
|
*/
|
|
243
235
|
export async function getUserRoleHierarchy(
|
|
244
|
-
userRoleId
|
|
236
|
+
userRoleId: string,
|
|
245
237
|
opts?: { defaultPublic?: boolean }
|
|
246
238
|
) {
|
|
247
239
|
// special case, if they don't have a role then they are a public user
|
|
@@ -265,9 +257,9 @@ export function checkForRoleResourceArray(
|
|
|
265
257
|
return rolePerms
|
|
266
258
|
}
|
|
267
259
|
|
|
268
|
-
export async function getAllRoleIds(appId
|
|
260
|
+
export async function getAllRoleIds(appId: string): Promise<string[]> {
|
|
269
261
|
const roles = await getAllRoles(appId)
|
|
270
|
-
return roles.map(role => role._id)
|
|
262
|
+
return roles.map(role => role._id!)
|
|
271
263
|
}
|
|
272
264
|
|
|
273
265
|
/**
|
|
@@ -18,7 +18,7 @@ export const account = (partial: Partial<Account> = {}): Account => {
|
|
|
18
18
|
return {
|
|
19
19
|
accountId: uuid(),
|
|
20
20
|
tenantId: generator.word(),
|
|
21
|
-
email: generator.email(),
|
|
21
|
+
email: generator.email({ domain: "example.com" }),
|
|
22
22
|
tenantName: generator.word(),
|
|
23
23
|
hosting: Hosting.SELF,
|
|
24
24
|
createdAt: Date.now(),
|
|
@@ -13,7 +13,7 @@ interface CreateUserRequestFields {
|
|
|
13
13
|
export function createUserRequest(userData?: Partial<CreateUserRequestFields>) {
|
|
14
14
|
const defaultValues = {
|
|
15
15
|
externalId: uuid(),
|
|
16
|
-
email:
|
|
16
|
+
email: `${uuid()}@example.com`,
|
|
17
17
|
firstName: generator.first(),
|
|
18
18
|
lastName: generator.last(),
|
|
19
19
|
username: generator.name(),
|