@directus/api 19.3.0 → 19.3.1
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/services/roles.js
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import { InvalidPayloadError, UnprocessableContentError } from '@directus/errors';
|
|
2
2
|
import { getMatch } from 'ip-matching';
|
|
3
|
+
import { omit } from 'lodash-es';
|
|
3
4
|
import { checkIncreasedUserLimits } from '../telemetry/utils/check-increased-user-limits.js';
|
|
4
5
|
import { getRoleCountsByUsers } from '../telemetry/utils/get-role-counts-by-users.js';
|
|
5
6
|
import {} from '../telemetry/utils/get-user-count.js';
|
|
6
7
|
import { getUserCountsByRoles } from '../telemetry/utils/get-user-counts-by-roles.js';
|
|
8
|
+
import { shouldCheckUserLimits } from '../telemetry/utils/should-check-user-limits.js';
|
|
9
|
+
import { shouldClearCache } from '../utils/should-clear-cache.js';
|
|
7
10
|
import { transaction } from '../utils/transaction.js';
|
|
8
11
|
import { ItemsService } from './items.js';
|
|
9
12
|
import { PermissionsService } from './permissions/index.js';
|
|
10
13
|
import { PresetsService } from './presets.js';
|
|
11
14
|
import { UsersService } from './users.js';
|
|
12
|
-
import { shouldClearCache } from '../utils/should-clear-cache.js';
|
|
13
|
-
import { omit } from 'lodash-es';
|
|
14
15
|
export class RolesService extends ItemsService {
|
|
15
16
|
constructor(options) {
|
|
16
17
|
super('directus_roles', options);
|
|
@@ -171,28 +172,31 @@ export class RolesService extends ItemsService {
|
|
|
171
172
|
}
|
|
172
173
|
async createOne(data, opts) {
|
|
173
174
|
this.assertValidIpAccess(data);
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
175
|
+
if (shouldCheckUserLimits()) {
|
|
176
|
+
const increasedCounts = {
|
|
177
|
+
admin: 0,
|
|
178
|
+
app: 0,
|
|
179
|
+
api: 0,
|
|
180
|
+
};
|
|
181
|
+
const existingIds = [];
|
|
182
|
+
if ('users' in data) {
|
|
183
|
+
const type = this.getRoleAccessType(data);
|
|
184
|
+
increasedCounts[type] += data['users'].length;
|
|
185
|
+
for (const user of data['users']) {
|
|
186
|
+
if (typeof user === 'string') {
|
|
187
|
+
existingIds.push(user);
|
|
188
|
+
}
|
|
189
|
+
else if (typeof user === 'object' && 'id' in user) {
|
|
190
|
+
existingIds.push(user['id']);
|
|
191
|
+
}
|
|
189
192
|
}
|
|
190
193
|
}
|
|
194
|
+
await checkIncreasedUserLimits(this.knex, increasedCounts, existingIds);
|
|
191
195
|
}
|
|
192
|
-
await checkIncreasedUserLimits(this.knex, increasedCounts, existingIds);
|
|
193
196
|
return super.createOne(data, opts);
|
|
194
197
|
}
|
|
195
198
|
async createMany(data, opts) {
|
|
199
|
+
const needsUserLimitCheck = shouldCheckUserLimits();
|
|
196
200
|
const increasedCounts = {
|
|
197
201
|
admin: 0,
|
|
198
202
|
app: 0,
|
|
@@ -201,7 +205,7 @@ export class RolesService extends ItemsService {
|
|
|
201
205
|
const existingIds = [];
|
|
202
206
|
for (const partialItem of data) {
|
|
203
207
|
this.assertValidIpAccess(partialItem);
|
|
204
|
-
if ('users' in partialItem) {
|
|
208
|
+
if (needsUserLimitCheck && 'users' in partialItem) {
|
|
205
209
|
const type = this.getRoleAccessType(partialItem);
|
|
206
210
|
increasedCounts[type] += partialItem['users'].length;
|
|
207
211
|
for (const user of partialItem['users']) {
|
|
@@ -214,121 +218,127 @@ export class RolesService extends ItemsService {
|
|
|
214
218
|
}
|
|
215
219
|
}
|
|
216
220
|
}
|
|
217
|
-
|
|
221
|
+
if (needsUserLimitCheck) {
|
|
222
|
+
await checkIncreasedUserLimits(this.knex, increasedCounts, existingIds);
|
|
223
|
+
}
|
|
218
224
|
return super.createMany(data, opts);
|
|
219
225
|
}
|
|
220
226
|
async updateOne(key, data, opts) {
|
|
221
227
|
this.assertValidIpAccess(data);
|
|
222
228
|
try {
|
|
223
|
-
const increasedCounts = {
|
|
224
|
-
admin: 0,
|
|
225
|
-
app: 0,
|
|
226
|
-
api: 0,
|
|
227
|
-
};
|
|
228
|
-
let increasedUsers = 0;
|
|
229
|
-
const existingIds = [];
|
|
230
|
-
let existingRole = await this.knex
|
|
231
|
-
.count('directus_users.id', { as: 'count' })
|
|
232
|
-
.select('directus_roles.admin_access', 'directus_roles.app_access')
|
|
233
|
-
.from('directus_users')
|
|
234
|
-
.where('directus_roles.id', '=', key)
|
|
235
|
-
.andWhere('directus_users.status', '=', 'active')
|
|
236
|
-
.leftJoin('directus_roles', 'directus_users.role', '=', 'directus_roles.id')
|
|
237
|
-
.groupBy('directus_roles.admin_access', 'directus_roles.app_access')
|
|
238
|
-
.first();
|
|
239
|
-
if (!existingRole) {
|
|
240
|
-
try {
|
|
241
|
-
const role = (await this.knex
|
|
242
|
-
.select('admin_access', 'app_access')
|
|
243
|
-
.from('directus_roles')
|
|
244
|
-
.where('id', '=', key)
|
|
245
|
-
.first()) ?? { admin_access: null, app_access: null };
|
|
246
|
-
existingRole = { count: 0, ...role };
|
|
247
|
-
}
|
|
248
|
-
catch {
|
|
249
|
-
existingRole = { count: 0, admin_access: null, app_access: null };
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
229
|
if ('users' in data) {
|
|
253
230
|
await this.checkForOtherAdminUsers(key, data['users']);
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
231
|
+
}
|
|
232
|
+
if (shouldCheckUserLimits()) {
|
|
233
|
+
const increasedCounts = {
|
|
234
|
+
admin: 0,
|
|
235
|
+
app: 0,
|
|
236
|
+
api: 0,
|
|
237
|
+
};
|
|
238
|
+
let increasedUsers = 0;
|
|
239
|
+
const existingIds = [];
|
|
240
|
+
let existingRole = await this.knex
|
|
241
|
+
.count('directus_users.id', { as: 'count' })
|
|
242
|
+
.select('directus_roles.admin_access', 'directus_roles.app_access')
|
|
243
|
+
.from('directus_users')
|
|
244
|
+
.where('directus_roles.id', '=', key)
|
|
245
|
+
.andWhere('directus_users.status', '=', 'active')
|
|
246
|
+
.leftJoin('directus_roles', 'directus_users.role', '=', 'directus_roles.id')
|
|
247
|
+
.groupBy('directus_roles.admin_access', 'directus_roles.app_access')
|
|
248
|
+
.first();
|
|
249
|
+
if (!existingRole) {
|
|
250
|
+
try {
|
|
251
|
+
const role = (await this.knex
|
|
252
|
+
.select('admin_access', 'app_access')
|
|
253
|
+
.from('directus_roles')
|
|
254
|
+
.where('id', '=', key)
|
|
255
|
+
.first()) ?? { admin_access: null, app_access: null };
|
|
256
|
+
existingRole = { count: 0, ...role };
|
|
257
|
+
}
|
|
258
|
+
catch {
|
|
259
|
+
existingRole = { count: 0, admin_access: null, app_access: null };
|
|
264
260
|
}
|
|
265
261
|
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
if (user['status'] === 'active') {
|
|
274
|
-
increasedUsers++;
|
|
262
|
+
if ('users' in data) {
|
|
263
|
+
const users = data['users'];
|
|
264
|
+
if (Array.isArray(users)) {
|
|
265
|
+
increasedUsers = users.length - Number(existingRole.count);
|
|
266
|
+
for (const user of users) {
|
|
267
|
+
if (typeof user === 'string') {
|
|
268
|
+
existingIds.push(user);
|
|
275
269
|
}
|
|
276
|
-
else {
|
|
277
|
-
|
|
270
|
+
else if (typeof user === 'object' && 'id' in user) {
|
|
271
|
+
existingIds.push(user['id']);
|
|
278
272
|
}
|
|
279
273
|
}
|
|
280
|
-
userIds.push(user.id);
|
|
281
274
|
}
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
275
|
+
else {
|
|
276
|
+
increasedUsers += users.create.length;
|
|
277
|
+
increasedUsers -= users.delete.length;
|
|
278
|
+
const userIds = [];
|
|
279
|
+
for (const user of users.update) {
|
|
280
|
+
if ('status' in user) {
|
|
281
|
+
// account for users being activated and deactivated
|
|
282
|
+
if (user['status'] === 'active') {
|
|
283
|
+
increasedUsers++;
|
|
284
|
+
}
|
|
285
|
+
else {
|
|
286
|
+
increasedUsers--;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
userIds.push(user.id);
|
|
286
290
|
}
|
|
287
|
-
|
|
288
|
-
|
|
291
|
+
try {
|
|
292
|
+
const existingCounts = await getRoleCountsByUsers(this.knex, userIds);
|
|
293
|
+
if (existingRole.admin_access) {
|
|
294
|
+
increasedUsers += existingCounts.app + existingCounts.api;
|
|
295
|
+
}
|
|
296
|
+
else if (existingRole.app_access) {
|
|
297
|
+
increasedUsers += existingCounts.admin + existingCounts.api;
|
|
298
|
+
}
|
|
299
|
+
else {
|
|
300
|
+
increasedUsers += existingCounts.admin + existingCounts.app;
|
|
301
|
+
}
|
|
289
302
|
}
|
|
290
|
-
|
|
291
|
-
|
|
303
|
+
catch {
|
|
304
|
+
// ignore failed user call
|
|
292
305
|
}
|
|
293
306
|
}
|
|
294
|
-
catch {
|
|
295
|
-
// ignore failed user call
|
|
296
|
-
}
|
|
297
307
|
}
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
308
|
+
let isAccessChanged = false;
|
|
309
|
+
let accessType = 'api';
|
|
310
|
+
if ('app_access' in data) {
|
|
311
|
+
if (data['app_access'] === true) {
|
|
312
|
+
accessType = 'app';
|
|
313
|
+
if (!existingRole.app_access)
|
|
314
|
+
isAccessChanged = true;
|
|
315
|
+
}
|
|
316
|
+
else if (existingRole.app_access) {
|
|
305
317
|
isAccessChanged = true;
|
|
318
|
+
}
|
|
306
319
|
}
|
|
307
320
|
else if (existingRole.app_access) {
|
|
308
|
-
|
|
321
|
+
accessType = 'app';
|
|
309
322
|
}
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
if (!existingRole.admin_access)
|
|
323
|
+
if ('admin_access' in data) {
|
|
324
|
+
if (data['admin_access'] === true) {
|
|
325
|
+
accessType = 'admin';
|
|
326
|
+
if (!existingRole.admin_access)
|
|
327
|
+
isAccessChanged = true;
|
|
328
|
+
}
|
|
329
|
+
else if (existingRole.admin_access) {
|
|
318
330
|
isAccessChanged = true;
|
|
331
|
+
}
|
|
319
332
|
}
|
|
320
333
|
else if (existingRole.admin_access) {
|
|
321
|
-
|
|
334
|
+
accessType = 'admin';
|
|
322
335
|
}
|
|
336
|
+
if (isAccessChanged) {
|
|
337
|
+
increasedCounts[accessType] += Number(existingRole.count);
|
|
338
|
+
}
|
|
339
|
+
increasedCounts[accessType] += increasedUsers;
|
|
340
|
+
await checkIncreasedUserLimits(this.knex, increasedCounts, existingIds);
|
|
323
341
|
}
|
|
324
|
-
else if (existingRole.admin_access) {
|
|
325
|
-
accessType = 'admin';
|
|
326
|
-
}
|
|
327
|
-
if (isAccessChanged) {
|
|
328
|
-
increasedCounts[accessType] += Number(existingRole.count);
|
|
329
|
-
}
|
|
330
|
-
increasedCounts[accessType] += increasedUsers;
|
|
331
|
-
await checkIncreasedUserLimits(this.knex, increasedCounts, existingIds);
|
|
332
342
|
}
|
|
333
343
|
catch (err) {
|
|
334
344
|
(opts || (opts = {})).preMutationError = err;
|
|
@@ -370,7 +380,7 @@ export class RolesService extends ItemsService {
|
|
|
370
380
|
if ('admin_access' in data && data['admin_access'] === false) {
|
|
371
381
|
await this.checkForOtherAdminRoles(keys);
|
|
372
382
|
}
|
|
373
|
-
if ('admin_access' in data || 'app_access' in data) {
|
|
383
|
+
if (shouldCheckUserLimits() && ('admin_access' in data || 'app_access' in data)) {
|
|
374
384
|
const existingCounts = await getUserCountsByRoles(this.knex, keys);
|
|
375
385
|
const increasedCounts = {
|
|
376
386
|
admin: 0,
|
package/dist/services/users.js
CHANGED
|
@@ -12,6 +12,7 @@ import { checkIncreasedUserLimits } from '../telemetry/utils/check-increased-use
|
|
|
12
12
|
import { getRoleCountsByRoles } from '../telemetry/utils/get-role-counts-by-roles.js';
|
|
13
13
|
import { getRoleCountsByUsers } from '../telemetry/utils/get-role-counts-by-users.js';
|
|
14
14
|
import {} from '../telemetry/utils/get-user-count.js';
|
|
15
|
+
import { shouldCheckUserLimits } from '../telemetry/utils/should-check-user-limits.js';
|
|
15
16
|
import { getSecret } from '../utils/get-secret.js';
|
|
16
17
|
import isUrlAllowed from '../utils/is-url-allowed.js';
|
|
17
18
|
import { verifyJWT } from '../utils/jwt.js';
|
|
@@ -177,7 +178,7 @@ export class UsersService extends ItemsService {
|
|
|
177
178
|
if (passwords.length) {
|
|
178
179
|
await this.checkPasswordPolicy(passwords);
|
|
179
180
|
}
|
|
180
|
-
if (roles.length) {
|
|
181
|
+
if (shouldCheckUserLimits() && roles.length) {
|
|
181
182
|
const increasedCounts = {
|
|
182
183
|
admin: 0,
|
|
183
184
|
app: 0,
|
|
@@ -248,6 +249,7 @@ export class UsersService extends ItemsService {
|
|
|
248
249
|
*/
|
|
249
250
|
async updateMany(keys, data, opts) {
|
|
250
251
|
try {
|
|
252
|
+
const needsUserLimitCheck = shouldCheckUserLimits();
|
|
251
253
|
if (data['role']) {
|
|
252
254
|
/*
|
|
253
255
|
* data['role'] has the following cases:
|
|
@@ -270,7 +272,7 @@ export class UsersService extends ItemsService {
|
|
|
270
272
|
if (!newRole?.admin_access) {
|
|
271
273
|
await this.checkRemainingAdminExistence(keys);
|
|
272
274
|
}
|
|
273
|
-
if (newRole) {
|
|
275
|
+
if (needsUserLimitCheck && newRole) {
|
|
274
276
|
const existingCounts = await getRoleCountsByUsers(this.knex, keys);
|
|
275
277
|
const increasedCounts = {
|
|
276
278
|
admin: 0,
|
|
@@ -289,13 +291,13 @@ export class UsersService extends ItemsService {
|
|
|
289
291
|
await checkIncreasedUserLimits(this.knex, increasedCounts);
|
|
290
292
|
}
|
|
291
293
|
}
|
|
292
|
-
if (data['role'] === null) {
|
|
294
|
+
if (needsUserLimitCheck && data['role'] === null) {
|
|
293
295
|
await checkIncreasedUserLimits(this.knex, { admin: 0, app: 0, api: 1 });
|
|
294
296
|
}
|
|
295
297
|
if (data['status'] !== undefined && data['status'] !== 'active') {
|
|
296
298
|
await this.checkRemainingActiveAdmin(keys);
|
|
297
299
|
}
|
|
298
|
-
if (data['status'] === 'active') {
|
|
300
|
+
if (needsUserLimitCheck && data['status'] === 'active') {
|
|
299
301
|
const increasedCounts = await getRoleCountsByUsers(this.knex, keys, { inactiveUsers: true });
|
|
300
302
|
await checkIncreasedUserLimits(this.knex, increasedCounts);
|
|
301
303
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { useEnv } from '@directus/env';
|
|
2
|
+
/**
|
|
3
|
+
* Confirm whether user limits needs to be checked
|
|
4
|
+
*/
|
|
5
|
+
export function shouldCheckUserLimits() {
|
|
6
|
+
const env = useEnv();
|
|
7
|
+
if (Number(env['USERS_ADMIN_ACCESS_LIMIT']) !== Infinity ||
|
|
8
|
+
Number(env['USERS_APP_ACCESS_LIMIT']) !== Infinity ||
|
|
9
|
+
Number(env['USERS_API_ACCESS_LIMIT']) !== Infinity) {
|
|
10
|
+
return true;
|
|
11
|
+
}
|
|
12
|
+
return false;
|
|
13
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@directus/api",
|
|
3
|
-
"version": "19.3.
|
|
3
|
+
"version": "19.3.1",
|
|
4
4
|
"description": "Directus is a real-time API and App dashboard for managing SQL database content",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"directus",
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"@rollup/plugin-node-resolve": "15.2.3",
|
|
66
66
|
"@rollup/plugin-virtual": "3.0.2",
|
|
67
67
|
"@types/cookie": "0.6.0",
|
|
68
|
-
"argon2": "0.40.
|
|
68
|
+
"argon2": "0.40.3",
|
|
69
69
|
"async": "3.2.5",
|
|
70
70
|
"axios": "1.7.2",
|
|
71
71
|
"busboy": "1.6.0",
|
|
@@ -96,7 +96,7 @@
|
|
|
96
96
|
"graphql-ws": "5.16.0",
|
|
97
97
|
"helmet": "7.1.0",
|
|
98
98
|
"icc": "3.0.0",
|
|
99
|
-
"inquirer": "9.2.
|
|
99
|
+
"inquirer": "9.2.23",
|
|
100
100
|
"ioredis": "5.4.1",
|
|
101
101
|
"ip-matching": "2.1.2",
|
|
102
102
|
"isolated-vm": "4.7.2",
|
|
@@ -140,35 +140,35 @@
|
|
|
140
140
|
"sharp": "0.33.3",
|
|
141
141
|
"snappy": "7.2.2",
|
|
142
142
|
"stream-json": "1.8.0",
|
|
143
|
-
"tar": "7.
|
|
144
|
-
"tsx": "4.
|
|
143
|
+
"tar": "7.2.0",
|
|
144
|
+
"tsx": "4.12.0",
|
|
145
145
|
"wellknown": "0.5.0",
|
|
146
146
|
"ws": "8.17.0",
|
|
147
147
|
"zod": "3.23.8",
|
|
148
148
|
"zod-validation-error": "3.2.0",
|
|
149
|
-
"@directus/app": "12.1.3",
|
|
150
|
-
"@directus/env": "1.1.6",
|
|
151
149
|
"@directus/constants": "11.0.4",
|
|
150
|
+
"@directus/app": "12.1.4",
|
|
151
|
+
"@directus/env": "1.1.6",
|
|
152
|
+
"@directus/extensions": "1.0.8",
|
|
152
153
|
"@directus/errors": "0.3.2",
|
|
153
|
-
"@directus/extensions": "1.0.
|
|
154
|
-
"@directus/extensions-
|
|
154
|
+
"@directus/extensions-registry": "1.0.8",
|
|
155
|
+
"@directus/extensions-sdk": "11.0.8",
|
|
155
156
|
"@directus/format-title": "10.1.2",
|
|
156
|
-
"@directus/extensions-sdk": "11.0.7",
|
|
157
157
|
"@directus/pressure": "1.0.20",
|
|
158
158
|
"@directus/memory": "1.0.9",
|
|
159
|
-
"@directus/schema": "11.0.
|
|
159
|
+
"@directus/schema": "11.0.3",
|
|
160
160
|
"@directus/specs": "10.2.10",
|
|
161
161
|
"@directus/storage": "10.0.13",
|
|
162
162
|
"@directus/storage-driver-azure": "10.0.22",
|
|
163
|
-
"@directus/storage-driver-cloudinary": "10.0.22",
|
|
164
163
|
"@directus/storage-driver-gcs": "10.0.23",
|
|
165
164
|
"@directus/storage-driver-local": "10.0.20",
|
|
166
|
-
"@directus/storage-driver-
|
|
165
|
+
"@directus/storage-driver-cloudinary": "10.0.22",
|
|
167
166
|
"@directus/storage-driver-s3": "10.0.23",
|
|
167
|
+
"@directus/storage-driver-supabase": "1.0.14",
|
|
168
168
|
"@directus/system-data": "1.0.4",
|
|
169
169
|
"@directus/utils": "11.0.9",
|
|
170
|
-
"directus": "
|
|
171
|
-
"
|
|
170
|
+
"@directus/validation": "0.0.17",
|
|
171
|
+
"directus": "10.12.1"
|
|
172
172
|
},
|
|
173
173
|
"devDependencies": {
|
|
174
174
|
"@ngneat/falso": "7.2.0",
|
|
@@ -182,7 +182,7 @@
|
|
|
182
182
|
"@types/destroy": "1.0.3",
|
|
183
183
|
"@types/encodeurl": "1.0.2",
|
|
184
184
|
"@types/express": "4.17.21",
|
|
185
|
-
"@types/express-serve-static-core": "4.19.
|
|
185
|
+
"@types/express-serve-static-core": "4.19.3",
|
|
186
186
|
"@types/fs-extra": "11.0.4",
|
|
187
187
|
"@types/glob-to-regexp": "0.4.4",
|
|
188
188
|
"@types/inquirer": "9.0.7",
|
|
@@ -211,7 +211,7 @@
|
|
|
211
211
|
"vitest": "1.5.3",
|
|
212
212
|
"@directus/random": "0.2.8",
|
|
213
213
|
"@directus/tsconfig": "1.0.1",
|
|
214
|
-
"@directus/types": "11.1.
|
|
214
|
+
"@directus/types": "11.1.3"
|
|
215
215
|
},
|
|
216
216
|
"optionalDependencies": {
|
|
217
217
|
"@keyv/redis": "2.8.4",
|