directus 9.22.3 → 9.23.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/app.js +5 -4
- package/dist/auth/drivers/ldap.d.ts +2 -2
- package/dist/auth/drivers/ldap.js +8 -8
- package/dist/auth/drivers/oauth2.js +2 -2
- package/dist/auth/drivers/openid.js +2 -2
- package/dist/cache.js +4 -4
- package/dist/cli/commands/schema/apply.js +19 -17
- package/dist/cli/utils/create-db-connection.d.ts +2 -1
- package/dist/cli/utils/create-env/env-stub.liquid +1 -1
- package/dist/cli/utils/drivers.d.ts +3 -9
- package/dist/constants.d.ts +2 -8
- package/dist/constants.js +3 -7
- package/dist/controllers/assets.js +5 -5
- package/dist/controllers/extensions.js +7 -7
- package/dist/controllers/files.js +1 -1
- package/dist/controllers/graphql.js +8 -0
- package/dist/controllers/schema.d.ts +2 -0
- package/dist/controllers/schema.js +98 -0
- package/dist/database/helpers/schema/dialects/cockroachdb.d.ts +1 -1
- package/dist/database/helpers/schema/dialects/oracle.d.ts +4 -1
- package/dist/database/helpers/schema/dialects/oracle.js +25 -0
- package/dist/database/helpers/schema/types.d.ts +8 -6
- package/dist/database/helpers/schema/types.js +7 -1
- package/dist/database/index.d.ts +2 -1
- package/dist/database/run-ast.js +2 -2
- package/dist/env.js +9 -2
- package/dist/extensions.js +1 -1
- package/dist/flows.js +17 -8
- package/dist/middleware/cache.js +2 -2
- package/dist/middleware/respond.js +14 -9
- package/dist/operations/item-create/index.test.d.ts +1 -0
- package/dist/operations/item-update/index.test.d.ts +1 -0
- package/dist/operations/log/index.test.d.ts +1 -0
- package/dist/operations/notification/index.test.d.ts +1 -0
- package/dist/operations/request/index.js +2 -1
- package/dist/operations/request/index.test.d.ts +1 -0
- package/dist/operations/sleep/index.test.d.ts +1 -0
- package/dist/operations/transform/index.test.d.ts +1 -0
- package/dist/operations/trigger/index.d.ts +2 -0
- package/dist/operations/trigger/index.js +26 -9
- package/dist/operations/trigger/index.test.d.ts +1 -0
- package/dist/request/index.d.ts +5 -0
- package/dist/request/index.js +18 -0
- package/dist/request/index.test.d.ts +1 -0
- package/dist/request/request-interceptor.d.ts +2 -0
- package/dist/request/request-interceptor.js +33 -0
- package/dist/request/request-interceptor.test.d.ts +1 -0
- package/dist/request/response-interceptor.d.ts +2 -0
- package/dist/request/response-interceptor.js +9 -0
- package/dist/request/response-interceptor.test.d.ts +1 -0
- package/dist/request/validate-ip.d.ts +1 -0
- package/dist/request/validate-ip.js +27 -0
- package/dist/request/validate-ip.test.d.ts +1 -0
- package/dist/services/assets.d.ts +1 -1
- package/dist/services/assets.js +11 -2
- package/dist/services/authentication.js +5 -5
- package/dist/services/fields.js +1 -0
- package/dist/services/files.js +44 -88
- package/dist/services/graphql/index.js +14 -8
- package/dist/services/graphql/utils/process-error.js +22 -9
- package/dist/services/import-export.d.ts +4 -2
- package/dist/services/import-export.js +17 -3
- package/dist/services/import-export.test.d.ts +1 -0
- package/dist/services/index.d.ts +1 -0
- package/dist/services/index.js +1 -0
- package/dist/services/items.js +34 -15
- package/dist/services/relations.js +2 -0
- package/dist/services/roles.js +32 -11
- package/dist/services/schema.d.ts +15 -0
- package/dist/services/schema.js +58 -0
- package/dist/services/schema.test.d.ts +1 -0
- package/dist/services/shares.d.ts +2 -2
- package/dist/services/shares.js +9 -9
- package/dist/services/users.js +74 -47
- package/dist/types/assets.d.ts +1 -1
- package/dist/types/database.d.ts +3 -0
- package/dist/types/database.js +4 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.js +1 -0
- package/dist/types/items.d.ts +5 -0
- package/dist/types/snapshot.d.ts +22 -0
- package/dist/types/snapshot.js +14 -0
- package/dist/utils/apply-diff.d.ts +9 -0
- package/dist/utils/apply-diff.js +259 -0
- package/dist/utils/apply-diff.test.d.ts +1 -0
- package/dist/utils/apply-query.js +8 -6
- package/dist/utils/apply-snapshot.d.ts +1 -3
- package/dist/utils/apply-snapshot.js +4 -234
- package/dist/utils/get-cache-headers.d.ts +3 -1
- package/dist/utils/get-cache-headers.js +20 -19
- package/dist/utils/get-cache-headers.test.d.ts +1 -0
- package/dist/utils/get-milliseconds.d.ts +4 -0
- package/dist/utils/get-milliseconds.js +15 -0
- package/dist/utils/get-milliseconds.test.d.ts +1 -0
- package/dist/utils/get-snapshot-diff.js +11 -7
- package/dist/utils/get-snapshot.js +29 -6
- package/dist/utils/get-versioned-hash.d.ts +1 -0
- package/dist/utils/get-versioned-hash.js +12 -0
- package/dist/utils/get-versioned-hash.test.d.ts +1 -0
- package/dist/utils/map-values-deep.d.ts +1 -0
- package/dist/utils/map-values-deep.js +29 -0
- package/dist/utils/map-values-deep.test.d.ts +1 -0
- package/dist/utils/sanitize-schema.d.ts +30 -0
- package/dist/utils/sanitize-schema.js +80 -0
- package/dist/utils/sanitize-schema.test.d.ts +1 -0
- package/dist/utils/track.js +3 -3
- package/dist/utils/url.js +2 -6
- package/dist/utils/url.test.d.ts +1 -0
- package/dist/utils/validate-diff.d.ts +7 -0
- package/dist/utils/validate-diff.js +114 -0
- package/dist/utils/validate-diff.test.d.ts +1 -0
- package/dist/utils/validate-query.js +2 -2
- package/dist/utils/validate-query.test.d.ts +1 -0
- package/dist/utils/validate-snapshot.d.ts +5 -0
- package/dist/utils/validate-snapshot.js +71 -0
- package/dist/utils/validate-snapshot.test.d.ts +1 -0
- package/dist/utils/with-timeout.d.ts +1 -0
- package/dist/utils/with-timeout.js +16 -0
- package/dist/webhooks.js +3 -2
- package/package.json +54 -53
package/dist/services/roles.js
CHANGED
|
@@ -56,8 +56,13 @@ class RolesService extends items_1.ItemsService {
|
|
|
56
56
|
return;
|
|
57
57
|
}
|
|
58
58
|
async updateOne(key, data, opts) {
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
try {
|
|
60
|
+
if ('users' in data) {
|
|
61
|
+
await this.checkForOtherAdminUsers(key, data.users);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
(opts || (opts = {})).preMutationException = err;
|
|
61
66
|
}
|
|
62
67
|
return super.updateOne(key, data, opts);
|
|
63
68
|
}
|
|
@@ -65,14 +70,24 @@ class RolesService extends items_1.ItemsService {
|
|
|
65
70
|
const primaryKeyField = this.schema.collections[this.collection].primary;
|
|
66
71
|
const keys = data.map((item) => item[primaryKeyField]);
|
|
67
72
|
const setsToNoAdmin = data.some((item) => item.admin_access === false);
|
|
68
|
-
|
|
69
|
-
|
|
73
|
+
try {
|
|
74
|
+
if (setsToNoAdmin) {
|
|
75
|
+
await this.checkForOtherAdminRoles(keys);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
catch (err) {
|
|
79
|
+
(opts || (opts = {})).preMutationException = err;
|
|
70
80
|
}
|
|
71
81
|
return super.updateBatch(data, opts);
|
|
72
82
|
}
|
|
73
83
|
async updateMany(keys, data, opts) {
|
|
74
|
-
|
|
75
|
-
|
|
84
|
+
try {
|
|
85
|
+
if ('admin_access' in data && data.admin_access === false) {
|
|
86
|
+
await this.checkForOtherAdminRoles(keys);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
catch (err) {
|
|
90
|
+
(opts || (opts = {})).preMutationException = err;
|
|
76
91
|
}
|
|
77
92
|
return super.updateMany(keys, data, opts);
|
|
78
93
|
}
|
|
@@ -81,7 +96,13 @@ class RolesService extends items_1.ItemsService {
|
|
|
81
96
|
return key;
|
|
82
97
|
}
|
|
83
98
|
async deleteMany(keys) {
|
|
84
|
-
|
|
99
|
+
const opts = {};
|
|
100
|
+
try {
|
|
101
|
+
await this.checkForOtherAdminRoles(keys);
|
|
102
|
+
}
|
|
103
|
+
catch (err) {
|
|
104
|
+
opts.preMutationException = err;
|
|
105
|
+
}
|
|
85
106
|
await this.knex.transaction(async (trx) => {
|
|
86
107
|
const itemsService = new items_1.ItemsService('directus_roles', {
|
|
87
108
|
knex: trx,
|
|
@@ -106,17 +127,17 @@ class RolesService extends items_1.ItemsService {
|
|
|
106
127
|
// Delete permissions/presets for this role, suspend all remaining users in role
|
|
107
128
|
await permissionsService.deleteByQuery({
|
|
108
129
|
filter: { role: { _in: keys } },
|
|
109
|
-
});
|
|
130
|
+
}, opts);
|
|
110
131
|
await presetsService.deleteByQuery({
|
|
111
132
|
filter: { role: { _in: keys } },
|
|
112
|
-
});
|
|
133
|
+
}, opts);
|
|
113
134
|
await usersService.updateByQuery({
|
|
114
135
|
filter: { role: { _in: keys } },
|
|
115
136
|
}, {
|
|
116
137
|
status: 'suspended',
|
|
117
138
|
role: null,
|
|
118
|
-
});
|
|
119
|
-
await itemsService.deleteMany(keys);
|
|
139
|
+
}, opts);
|
|
140
|
+
await itemsService.deleteMany(keys, opts);
|
|
120
141
|
});
|
|
121
142
|
return keys;
|
|
122
143
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Accountability } from '@directus/shared/types';
|
|
2
|
+
import { Knex } from 'knex';
|
|
3
|
+
import { AbstractServiceOptions, Snapshot, SnapshotDiff, SnapshotDiffWithHash, SnapshotWithHash } from '../types';
|
|
4
|
+
export declare class SchemaService {
|
|
5
|
+
knex: Knex;
|
|
6
|
+
accountability: Accountability | null;
|
|
7
|
+
constructor(options: Omit<AbstractServiceOptions, 'schema'>);
|
|
8
|
+
snapshot(): Promise<Snapshot>;
|
|
9
|
+
apply(payload: SnapshotDiffWithHash): Promise<void>;
|
|
10
|
+
diff(snapshot: Snapshot, options?: {
|
|
11
|
+
currentSnapshot?: Snapshot;
|
|
12
|
+
force?: boolean;
|
|
13
|
+
}): Promise<SnapshotDiff | null>;
|
|
14
|
+
getHashedSnapshot(snapshot: Snapshot): SnapshotWithHash;
|
|
15
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.SchemaService = void 0;
|
|
7
|
+
const database_1 = __importDefault(require("../database"));
|
|
8
|
+
const exceptions_1 = require("../exceptions");
|
|
9
|
+
const apply_diff_1 = require("../utils/apply-diff");
|
|
10
|
+
const get_snapshot_1 = require("../utils/get-snapshot");
|
|
11
|
+
const get_snapshot_diff_1 = require("../utils/get-snapshot-diff");
|
|
12
|
+
const get_versioned_hash_1 = require("../utils/get-versioned-hash");
|
|
13
|
+
const validate_diff_1 = require("../utils/validate-diff");
|
|
14
|
+
const validate_snapshot_1 = require("../utils/validate-snapshot");
|
|
15
|
+
class SchemaService {
|
|
16
|
+
constructor(options) {
|
|
17
|
+
var _a, _b;
|
|
18
|
+
this.knex = (_a = options.knex) !== null && _a !== void 0 ? _a : (0, database_1.default)();
|
|
19
|
+
this.accountability = (_b = options.accountability) !== null && _b !== void 0 ? _b : null;
|
|
20
|
+
}
|
|
21
|
+
async snapshot() {
|
|
22
|
+
var _a;
|
|
23
|
+
if (((_a = this.accountability) === null || _a === void 0 ? void 0 : _a.admin) !== true)
|
|
24
|
+
throw new exceptions_1.ForbiddenException();
|
|
25
|
+
const currentSnapshot = await (0, get_snapshot_1.getSnapshot)({ database: this.knex });
|
|
26
|
+
return currentSnapshot;
|
|
27
|
+
}
|
|
28
|
+
async apply(payload) {
|
|
29
|
+
var _a;
|
|
30
|
+
if (((_a = this.accountability) === null || _a === void 0 ? void 0 : _a.admin) !== true)
|
|
31
|
+
throw new exceptions_1.ForbiddenException();
|
|
32
|
+
const currentSnapshot = await this.snapshot();
|
|
33
|
+
const snapshotWithHash = this.getHashedSnapshot(currentSnapshot);
|
|
34
|
+
if (!(0, validate_diff_1.validateApplyDiff)(payload, snapshotWithHash))
|
|
35
|
+
return;
|
|
36
|
+
await (0, apply_diff_1.applyDiff)(currentSnapshot, payload.diff, { database: this.knex });
|
|
37
|
+
}
|
|
38
|
+
async diff(snapshot, options) {
|
|
39
|
+
var _a, _b;
|
|
40
|
+
if (((_a = this.accountability) === null || _a === void 0 ? void 0 : _a.admin) !== true)
|
|
41
|
+
throw new exceptions_1.ForbiddenException();
|
|
42
|
+
(0, validate_snapshot_1.validateSnapshot)(snapshot, options === null || options === void 0 ? void 0 : options.force);
|
|
43
|
+
const currentSnapshot = (_b = options === null || options === void 0 ? void 0 : options.currentSnapshot) !== null && _b !== void 0 ? _b : (await (0, get_snapshot_1.getSnapshot)({ database: this.knex }));
|
|
44
|
+
const diff = (0, get_snapshot_diff_1.getSnapshotDiff)(currentSnapshot, snapshot);
|
|
45
|
+
if (diff.collections.length === 0 && diff.fields.length === 0 && diff.relations.length === 0) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
return diff;
|
|
49
|
+
}
|
|
50
|
+
getHashedSnapshot(snapshot) {
|
|
51
|
+
const snapshotHash = (0, get_versioned_hash_1.getVersionedHash)(snapshot);
|
|
52
|
+
return {
|
|
53
|
+
...snapshot,
|
|
54
|
+
hash: snapshotHash,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
exports.SchemaService = SchemaService;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { AbstractServiceOptions, LoginResult,
|
|
2
|
-
import { ItemsService } from './items';
|
|
1
|
+
import { AbstractServiceOptions, Item, LoginResult, MutationOptions, PrimaryKey } from '../types';
|
|
3
2
|
import { AuthorizationService } from './authorization';
|
|
3
|
+
import { ItemsService } from './items';
|
|
4
4
|
export declare class SharesService extends ItemsService {
|
|
5
5
|
authorizationService: AuthorizationService;
|
|
6
6
|
constructor(options: AbstractServiceOptions);
|
package/dist/services/shares.js
CHANGED
|
@@ -4,18 +4,18 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.SharesService = void 0;
|
|
7
|
-
const items_1 = require("./items");
|
|
8
7
|
const argon2_1 = __importDefault(require("argon2"));
|
|
9
8
|
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
|
|
10
|
-
const ms_1 = __importDefault(require("ms"));
|
|
11
|
-
const exceptions_1 = require("../exceptions");
|
|
12
9
|
const env_1 = __importDefault(require("../env"));
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
const mail_1 = require("./mail");
|
|
16
|
-
const user_name_1 = require("../utils/user-name");
|
|
10
|
+
const exceptions_1 = require("../exceptions");
|
|
11
|
+
const get_milliseconds_1 = require("../utils/get-milliseconds");
|
|
17
12
|
const md_1 = require("../utils/md");
|
|
18
13
|
const url_1 = require("../utils/url");
|
|
14
|
+
const user_name_1 = require("../utils/user-name");
|
|
15
|
+
const authorization_1 = require("./authorization");
|
|
16
|
+
const items_1 = require("./items");
|
|
17
|
+
const mail_1 = require("./mail");
|
|
18
|
+
const users_1 = require("./users");
|
|
19
19
|
class SharesService extends items_1.ItemsService {
|
|
20
20
|
constructor(options) {
|
|
21
21
|
super('directus_shares', options);
|
|
@@ -80,7 +80,7 @@ class SharesService extends items_1.ItemsService {
|
|
|
80
80
|
issuer: 'directus',
|
|
81
81
|
});
|
|
82
82
|
const refreshToken = nanoid(64);
|
|
83
|
-
const refreshTokenExpiration = new Date(Date.now() + (0,
|
|
83
|
+
const refreshTokenExpiration = new Date(Date.now() + (0, get_milliseconds_1.getMilliseconds)(env_1.default.REFRESH_TOKEN_TTL, 0));
|
|
84
84
|
await this.knex('directus_sessions').insert({
|
|
85
85
|
token: refreshToken,
|
|
86
86
|
expires: refreshTokenExpiration,
|
|
@@ -93,7 +93,7 @@ class SharesService extends items_1.ItemsService {
|
|
|
93
93
|
return {
|
|
94
94
|
accessToken,
|
|
95
95
|
refreshToken,
|
|
96
|
-
expires: (0,
|
|
96
|
+
expires: (0, get_milliseconds_1.getMilliseconds)(env_1.default.ACCESS_TOKEN_TTL),
|
|
97
97
|
};
|
|
98
98
|
}
|
|
99
99
|
/**
|
package/dist/services/users.js
CHANGED
|
@@ -131,11 +131,16 @@ class UsersService extends items_1.ItemsService {
|
|
|
131
131
|
async createMany(data, opts) {
|
|
132
132
|
const emails = data.map((payload) => payload.email).filter((email) => email);
|
|
133
133
|
const passwords = data.map((payload) => payload.password).filter((password) => password);
|
|
134
|
-
|
|
135
|
-
|
|
134
|
+
try {
|
|
135
|
+
if (emails.length) {
|
|
136
|
+
await this.checkUniqueEmails(emails);
|
|
137
|
+
}
|
|
138
|
+
if (passwords.length) {
|
|
139
|
+
await this.checkPasswordPolicy(passwords);
|
|
140
|
+
}
|
|
136
141
|
}
|
|
137
|
-
|
|
138
|
-
|
|
142
|
+
catch (err) {
|
|
143
|
+
(opts || (opts = {})).preMutationException = err;
|
|
139
144
|
}
|
|
140
145
|
return await super.createMany(data, opts);
|
|
141
146
|
}
|
|
@@ -175,44 +180,49 @@ class UsersService extends items_1.ItemsService {
|
|
|
175
180
|
*/
|
|
176
181
|
async updateMany(keys, data, opts) {
|
|
177
182
|
var _a, _b;
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
183
|
+
try {
|
|
184
|
+
if (data.role) {
|
|
185
|
+
// data.role will be an object with id with GraphQL mutations
|
|
186
|
+
const roleId = (_b = (_a = data.role) === null || _a === void 0 ? void 0 : _a.id) !== null && _b !== void 0 ? _b : data.role;
|
|
187
|
+
const newRole = await this.knex.select('admin_access').from('directus_roles').where('id', roleId).first();
|
|
188
|
+
if (!(newRole === null || newRole === void 0 ? void 0 : newRole.admin_access)) {
|
|
189
|
+
await this.checkRemainingAdminExistence(keys);
|
|
190
|
+
}
|
|
184
191
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
await this.checkRemainingActiveAdmin(keys);
|
|
188
|
-
}
|
|
189
|
-
if (data.email) {
|
|
190
|
-
if (keys.length > 1) {
|
|
191
|
-
throw new record_not_unique_1.RecordNotUniqueException('email', {
|
|
192
|
-
collection: 'directus_users',
|
|
193
|
-
field: 'email',
|
|
194
|
-
invalid: data.email,
|
|
195
|
-
});
|
|
192
|
+
if (data.status !== undefined && data.status !== 'active') {
|
|
193
|
+
await this.checkRemainingActiveAdmin(keys);
|
|
196
194
|
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
if (this.accountability && this.accountability.admin !== true) {
|
|
207
|
-
throw new exceptions_2.InvalidPayloadException(`You can't change the "provider" value manually.`);
|
|
195
|
+
if (data.email) {
|
|
196
|
+
if (keys.length > 1) {
|
|
197
|
+
throw new record_not_unique_1.RecordNotUniqueException('email', {
|
|
198
|
+
collection: 'directus_users',
|
|
199
|
+
field: 'email',
|
|
200
|
+
invalid: data.email,
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
await this.checkUniqueEmails([data.email], keys[0]);
|
|
208
204
|
}
|
|
209
|
-
data.
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
if (
|
|
213
|
-
throw new exceptions_2.InvalidPayloadException(`You can't change the "
|
|
205
|
+
if (data.password) {
|
|
206
|
+
await this.checkPasswordPolicy([data.password]);
|
|
207
|
+
}
|
|
208
|
+
if (data.tfa_secret !== undefined) {
|
|
209
|
+
throw new exceptions_2.InvalidPayloadException(`You can't change the "tfa_secret" value manually.`);
|
|
214
210
|
}
|
|
215
|
-
data.
|
|
211
|
+
if (data.provider !== undefined) {
|
|
212
|
+
if (this.accountability && this.accountability.admin !== true) {
|
|
213
|
+
throw new exceptions_2.InvalidPayloadException(`You can't change the "provider" value manually.`);
|
|
214
|
+
}
|
|
215
|
+
data.auth_data = null;
|
|
216
|
+
}
|
|
217
|
+
if (data.external_identifier !== undefined) {
|
|
218
|
+
if (this.accountability && this.accountability.admin !== true) {
|
|
219
|
+
throw new exceptions_2.InvalidPayloadException(`You can't change the "external_identifier" value manually.`);
|
|
220
|
+
}
|
|
221
|
+
data.auth_data = null;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
catch (err) {
|
|
225
|
+
(opts || (opts = {})).preMutationException = err;
|
|
216
226
|
}
|
|
217
227
|
return await super.updateMany(keys, data, opts);
|
|
218
228
|
}
|
|
@@ -227,7 +237,12 @@ class UsersService extends items_1.ItemsService {
|
|
|
227
237
|
* Delete multiple users by primary key
|
|
228
238
|
*/
|
|
229
239
|
async deleteMany(keys, opts) {
|
|
230
|
-
|
|
240
|
+
try {
|
|
241
|
+
await this.checkRemainingAdminExistence(keys);
|
|
242
|
+
}
|
|
243
|
+
catch (err) {
|
|
244
|
+
(opts || (opts = {})).preMutationException = err;
|
|
245
|
+
}
|
|
231
246
|
await this.knex('directus_notifications').update({ sender: null }).whereIn('sender', keys);
|
|
232
247
|
await super.deleteMany(keys, opts);
|
|
233
248
|
return keys;
|
|
@@ -248,8 +263,14 @@ class UsersService extends items_1.ItemsService {
|
|
|
248
263
|
return await this.deleteMany(keys, opts);
|
|
249
264
|
}
|
|
250
265
|
async inviteUser(email, role, url, subject) {
|
|
251
|
-
|
|
252
|
-
|
|
266
|
+
const opts = {};
|
|
267
|
+
try {
|
|
268
|
+
if (url && (0, is_url_allowed_1.default)(url, env_1.default.USER_INVITE_URL_ALLOW_LIST) === false) {
|
|
269
|
+
throw new exceptions_2.InvalidPayloadException(`Url "${url}" can't be used to invite users.`);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
catch (err) {
|
|
273
|
+
opts.preMutationException = err;
|
|
253
274
|
}
|
|
254
275
|
const emails = (0, utils_1.toArray)(email);
|
|
255
276
|
const mailService = new mail_1.MailService({
|
|
@@ -263,7 +284,7 @@ class UsersService extends items_1.ItemsService {
|
|
|
263
284
|
const inviteURL = url ? new url_1.Url(url) : new url_1.Url(env_1.default.PUBLIC_URL).addPath('admin', 'accept-invite');
|
|
264
285
|
inviteURL.setQuery('token', token);
|
|
265
286
|
// Create user first to verify uniqueness
|
|
266
|
-
await this.createOne({ email, role, status: 'invited' });
|
|
287
|
+
await this.createOne({ email, role, status: 'invited' }, opts);
|
|
267
288
|
await mailService.send({
|
|
268
289
|
to: email,
|
|
269
290
|
subject: subjectLine,
|
|
@@ -293,9 +314,6 @@ class UsersService extends items_1.ItemsService {
|
|
|
293
314
|
await service.updateOne(user.id, { password, status: 'active' });
|
|
294
315
|
}
|
|
295
316
|
async requestPasswordReset(email, url, subject) {
|
|
296
|
-
if (url && (0, is_url_allowed_1.default)(url, env_1.default.PASSWORD_RESET_URL_ALLOW_LIST) === false) {
|
|
297
|
-
throw new exceptions_2.InvalidPayloadException(`Url "${url}" can't be used to reset passwords.`);
|
|
298
|
-
}
|
|
299
317
|
const STALL_TIME = 500;
|
|
300
318
|
const timeStart = perf_hooks_1.performance.now();
|
|
301
319
|
const user = await this.knex
|
|
@@ -307,6 +325,9 @@ class UsersService extends items_1.ItemsService {
|
|
|
307
325
|
await (0, stall_1.stall)(STALL_TIME, timeStart);
|
|
308
326
|
throw new exceptions_2.ForbiddenException();
|
|
309
327
|
}
|
|
328
|
+
if (url && (0, is_url_allowed_1.default)(url, env_1.default.PASSWORD_RESET_URL_ALLOW_LIST) === false) {
|
|
329
|
+
throw new exceptions_2.InvalidPayloadException(`Url "${url}" can't be used to reset passwords.`);
|
|
330
|
+
}
|
|
310
331
|
const mailService = new mail_1.MailService({
|
|
311
332
|
schema: this.schema,
|
|
312
333
|
knex: this.knex,
|
|
@@ -336,7 +357,13 @@ class UsersService extends items_1.ItemsService {
|
|
|
336
357
|
const { email, scope, hash } = jsonwebtoken_1.default.verify(token, env_1.default.SECRET, { issuer: 'directus' });
|
|
337
358
|
if (scope !== 'password-reset' || !hash)
|
|
338
359
|
throw new exceptions_2.ForbiddenException();
|
|
339
|
-
|
|
360
|
+
const opts = {};
|
|
361
|
+
try {
|
|
362
|
+
await this.checkPasswordPolicy([password]);
|
|
363
|
+
}
|
|
364
|
+
catch (err) {
|
|
365
|
+
opts.preMutationException = err;
|
|
366
|
+
}
|
|
340
367
|
const user = await this.knex.select('id', 'status', 'password').from('directus_users').where({ email }).first();
|
|
341
368
|
if ((user === null || user === void 0 ? void 0 : user.status) !== 'active' || hash !== (0, utils_2.getSimpleHash)('' + user.password)) {
|
|
342
369
|
throw new exceptions_2.ForbiddenException();
|
|
@@ -350,7 +377,7 @@ class UsersService extends items_1.ItemsService {
|
|
|
350
377
|
admin: true, // We need to skip permissions checks for the update call below
|
|
351
378
|
},
|
|
352
379
|
});
|
|
353
|
-
await service.updateOne(user.id, { password, status: 'active' });
|
|
380
|
+
await service.updateOne(user.id, { password, status: 'active' }, opts);
|
|
354
381
|
}
|
|
355
382
|
}
|
|
356
383
|
exports.UsersService = UsersService;
|
package/dist/types/assets.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ResizeOptions, Sharp } from 'sharp';
|
|
2
2
|
export declare const TransformationMethods: readonly ["toFormat", "jpeg", "png", "tiff", "webp", "resize", "extend", "extract", "trim", "rotate", "flip", "flop", "sharpen", "median", "blur", "flatten", "gamma", "negate", "normalise", "normalize", "clahe", "convolve", "threshold", "linear", "recomb", "modulate", "tint", "greyscale", "grayscale", "toColorspace", "toColourspace", "removeAlpha", "ensureAlpha", "extractChannel", "bandbool"];
|
|
3
|
-
type AllowedSharpMethods = Pick<Sharp, typeof TransformationMethods[number]>;
|
|
3
|
+
type AllowedSharpMethods = Pick<Sharp, (typeof TransformationMethods)[number]>;
|
|
4
4
|
export type TransformationMap = {
|
|
5
5
|
[M in keyof AllowedSharpMethods]: readonly [M, ...Parameters<AllowedSharpMethods[M]>];
|
|
6
6
|
};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export type Driver = 'mysql' | 'pg' | 'cockroachdb' | 'sqlite3' | 'oracledb' | 'mssql';
|
|
2
|
+
export declare const DatabaseClients: readonly ["mysql", "postgres", "cockroachdb", "sqlite", "oracle", "mssql", "redshift"];
|
|
3
|
+
export type DatabaseClient = (typeof DatabaseClients)[number];
|
package/dist/types/index.d.ts
CHANGED
package/dist/types/index.js
CHANGED
|
@@ -18,6 +18,7 @@ __exportStar(require("./assets"), exports);
|
|
|
18
18
|
__exportStar(require("./ast"), exports);
|
|
19
19
|
__exportStar(require("./auth"), exports);
|
|
20
20
|
__exportStar(require("./collection"), exports);
|
|
21
|
+
__exportStar(require("./database"), exports);
|
|
21
22
|
__exportStar(require("./events"), exports);
|
|
22
23
|
__exportStar(require("./files"), exports);
|
|
23
24
|
__exportStar(require("./graphql"), exports);
|
package/dist/types/items.d.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* I know this looks a little silly, but it allows us to explicitly differentiate between when we're
|
|
3
3
|
* expecting an item vs any other generic object.
|
|
4
4
|
*/
|
|
5
|
+
import { BaseException } from '@directus/shared/exceptions';
|
|
5
6
|
import { EventContext } from '@directus/shared/types';
|
|
6
7
|
export type Item = Record<string, any>;
|
|
7
8
|
export type PrimaryKey = string | number;
|
|
@@ -36,6 +37,10 @@ export type MutationOptions = {
|
|
|
36
37
|
* Can be used to queue up the nested events from item service's create, update and delete
|
|
37
38
|
*/
|
|
38
39
|
bypassEmitAction?: (params: ActionEventParams) => void;
|
|
40
|
+
/**
|
|
41
|
+
* The validation error to throw right before the mutation takes place
|
|
42
|
+
*/
|
|
43
|
+
preMutationException?: BaseException;
|
|
39
44
|
};
|
|
40
45
|
export type ActionEventParams = {
|
|
41
46
|
event: string | string[];
|
package/dist/types/snapshot.d.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { Collection } from './collection';
|
|
2
2
|
import { Relation, RelationMeta, Field, FieldMeta } from '@directus/shared/types';
|
|
3
3
|
import { Diff } from 'deep-diff';
|
|
4
|
+
import { DatabaseClient } from './database';
|
|
4
5
|
export type Snapshot = {
|
|
5
6
|
version: number;
|
|
6
7
|
directus: string;
|
|
8
|
+
vendor?: DatabaseClient;
|
|
7
9
|
collections: Collection[];
|
|
8
10
|
fields: SnapshotField[];
|
|
9
11
|
relations: SnapshotRelation[];
|
|
@@ -14,6 +16,9 @@ export type SnapshotField = Field & {
|
|
|
14
16
|
export type SnapshotRelation = Relation & {
|
|
15
17
|
meta: Omit<RelationMeta, 'id'>;
|
|
16
18
|
};
|
|
19
|
+
export type SnapshotWithHash = Snapshot & {
|
|
20
|
+
hash: string;
|
|
21
|
+
};
|
|
17
22
|
export type SnapshotDiff = {
|
|
18
23
|
collections: {
|
|
19
24
|
collection: string;
|
|
@@ -31,3 +36,20 @@ export type SnapshotDiff = {
|
|
|
31
36
|
diff: Diff<SnapshotRelation | undefined>[];
|
|
32
37
|
}[];
|
|
33
38
|
};
|
|
39
|
+
export type SnapshotDiffWithHash = {
|
|
40
|
+
hash: string;
|
|
41
|
+
diff: SnapshotDiff;
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Indicates the kind of change based on comparisons by deep-diff package
|
|
45
|
+
*/
|
|
46
|
+
export declare const DiffKind: {
|
|
47
|
+
/** indicates a newly added property/element */
|
|
48
|
+
readonly NEW: "N";
|
|
49
|
+
/** indicates a property/element was deleted */
|
|
50
|
+
readonly DELETE: "D";
|
|
51
|
+
/** indicates a property/element was edited */
|
|
52
|
+
readonly EDIT: "E";
|
|
53
|
+
/** indicates a change occurred within an array */
|
|
54
|
+
readonly ARRAY: "A";
|
|
55
|
+
};
|
package/dist/types/snapshot.js
CHANGED
|
@@ -1,2 +1,16 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DiffKind = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Indicates the kind of change based on comparisons by deep-diff package
|
|
6
|
+
*/
|
|
7
|
+
exports.DiffKind = {
|
|
8
|
+
/** indicates a newly added property/element */
|
|
9
|
+
NEW: 'N',
|
|
10
|
+
/** indicates a property/element was deleted */
|
|
11
|
+
DELETE: 'D',
|
|
12
|
+
/** indicates a property/element was edited */
|
|
13
|
+
EDIT: 'E',
|
|
14
|
+
/** indicates a change occurred within an array */
|
|
15
|
+
ARRAY: 'A',
|
|
16
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { SchemaOverview } from '@directus/shared/types';
|
|
2
|
+
import { Knex } from 'knex';
|
|
3
|
+
import { Snapshot, SnapshotDiff, SnapshotField } from '../types';
|
|
4
|
+
import { Diff } from 'deep-diff';
|
|
5
|
+
export declare function applyDiff(currentSnapshot: Snapshot, snapshotDiff: SnapshotDiff, options?: {
|
|
6
|
+
database?: Knex;
|
|
7
|
+
schema?: SchemaOverview;
|
|
8
|
+
}): Promise<void>;
|
|
9
|
+
export declare function isNestedMetaUpdate(diff: Diff<SnapshotField | undefined>): boolean;
|