directus 9.2.0 → 9.4.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.
- package/dist/app.js +5 -3
- package/dist/auth/auth.d.ts +4 -6
- package/dist/auth/auth.js +5 -9
- package/dist/auth/drivers/ldap.d.ts +3 -3
- package/dist/auth/drivers/ldap.js +11 -4
- package/dist/auth/drivers/local.d.ts +2 -2
- package/dist/auth/drivers/local.js +5 -12
- package/dist/auth/drivers/oauth2.d.ts +4 -4
- package/dist/auth/drivers/oauth2.js +47 -21
- package/dist/auth/drivers/openid.d.ts +4 -4
- package/dist/auth/drivers/openid.js +35 -19
- package/dist/cli/commands/bootstrap/index.js +3 -2
- package/dist/cli/commands/init/index.js +3 -7
- package/dist/cli/commands/schema/apply.js +1 -1
- package/dist/cli/utils/defaults.d.ts +11 -0
- package/dist/cli/utils/defaults.js +14 -0
- package/dist/constants.d.ts +8 -0
- package/dist/constants.js +16 -2
- package/dist/controllers/shares.d.ts +2 -0
- package/dist/controllers/shares.js +212 -0
- package/dist/controllers/users.js +21 -9
- package/dist/database/migrations/20211211A-add-shares.d.ts +3 -0
- package/dist/database/migrations/20211211A-add-shares.js +37 -0
- package/dist/database/run-ast.js +5 -5
- package/dist/database/system-data/app-access-permissions/app-access-permissions.yaml +0 -15
- package/dist/database/system-data/app-access-permissions/index.d.ts +1 -0
- package/dist/database/system-data/app-access-permissions/index.js +4 -2
- package/dist/database/system-data/app-access-permissions/schema-access-permissions.yaml +17 -0
- package/dist/database/system-data/collections/collections.yaml +3 -0
- package/dist/database/system-data/fields/sessions.yaml +1 -1
- package/dist/database/system-data/fields/shares.yaml +73 -0
- package/dist/database/system-data/fields/users.yaml +1 -1
- package/dist/database/system-data/relations/relations.yaml +15 -0
- package/dist/emitter.d.ts +3 -2
- package/dist/emitter.js +13 -6
- package/dist/exceptions/index.d.ts +2 -0
- package/dist/exceptions/index.js +2 -0
- package/dist/exceptions/invalid-token.d.ts +4 -0
- package/dist/exceptions/invalid-token.js +10 -0
- package/dist/exceptions/unexpected-response.d.ts +4 -0
- package/dist/exceptions/unexpected-response.js +10 -0
- package/dist/extensions.d.ts +1 -0
- package/dist/extensions.js +10 -4
- package/dist/middleware/authenticate.js +5 -15
- package/dist/middleware/check-ip.js +9 -6
- package/dist/middleware/respond.js +4 -1
- package/dist/services/activity.d.ts +2 -1
- package/dist/services/activity.js +2 -2
- package/dist/services/authentication.d.ts +2 -7
- package/dist/services/authentication.js +81 -41
- package/dist/services/authorization.js +3 -3
- package/dist/services/collections.d.ts +1 -2
- package/dist/services/collections.js +2 -2
- package/dist/services/files.d.ts +2 -2
- package/dist/services/files.js +14 -8
- package/dist/services/graphql.js +16 -5
- package/dist/services/index.d.ts +1 -0
- package/dist/services/index.js +1 -0
- package/dist/services/items.d.ts +1 -15
- package/dist/services/notifications.d.ts +2 -2
- package/dist/services/permissions.d.ts +2 -2
- package/dist/services/roles.d.ts +2 -2
- package/dist/services/shares.d.ts +17 -0
- package/dist/services/shares.js +135 -0
- package/dist/services/specifications.js +1 -1
- package/dist/services/users.d.ts +2 -2
- package/dist/services/users.js +8 -6
- package/dist/services/webhooks.d.ts +2 -2
- package/dist/tests/database/migrations/run.test.d.ts +1 -0
- package/dist/tests/database/migrations/run.test.js +29 -0
- package/dist/types/ast.d.ts +3 -3
- package/dist/types/auth.d.ts +31 -0
- package/dist/types/extensions.d.ts +2 -0
- package/dist/types/items.d.ts +14 -0
- package/dist/utils/apply-query.d.ts +0 -38
- package/dist/utils/apply-query.js +67 -69
- package/dist/utils/apply-snapshot.js +69 -14
- package/dist/utils/get-ast-from-query.js +3 -3
- package/dist/utils/get-permissions.d.ts +2 -2
- package/dist/utils/get-permissions.js +117 -72
- package/dist/utils/get-relation-type.d.ts +1 -1
- package/dist/utils/get-relation-type.js +1 -1
- package/dist/utils/merge-permissions-for-share.d.ts +5 -0
- package/dist/utils/merge-permissions-for-share.js +116 -0
- package/dist/utils/merge-permissions.d.ts +13 -1
- package/dist/utils/merge-permissions.js +29 -21
- package/dist/utils/reduce-schema.d.ts +2 -2
- package/dist/utils/reduce-schema.js +7 -7
- package/dist/utils/user-name.js +3 -0
- package/package.json +14 -13
package/dist/services/graphql.js
CHANGED
|
@@ -34,6 +34,7 @@ const revisions_1 = require("./revisions");
|
|
|
34
34
|
const roles_1 = require("./roles");
|
|
35
35
|
const server_1 = require("./server");
|
|
36
36
|
const settings_1 = require("./settings");
|
|
37
|
+
const shares_1 = require("./shares");
|
|
37
38
|
const specifications_1 = require("./specifications");
|
|
38
39
|
const tfa_1 = require("./tfa");
|
|
39
40
|
const users_1 = require("./users");
|
|
@@ -112,15 +113,23 @@ class GraphQLService {
|
|
|
112
113
|
return formattedResult;
|
|
113
114
|
}
|
|
114
115
|
getSchema(type = 'schema') {
|
|
115
|
-
var _a, _b, _c, _d;
|
|
116
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
116
117
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
117
118
|
const self = this;
|
|
118
119
|
const schemaComposer = new graphql_compose_1.SchemaComposer();
|
|
119
120
|
const schema = {
|
|
120
|
-
read: ((_a = this.accountability) === null || _a === void 0 ? void 0 : _a.admin) === true
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
121
|
+
read: ((_a = this.accountability) === null || _a === void 0 ? void 0 : _a.admin) === true
|
|
122
|
+
? this.schema
|
|
123
|
+
: (0, reduce_schema_1.reduceSchema)(this.schema, ((_b = this.accountability) === null || _b === void 0 ? void 0 : _b.permissions) || null, ['read']),
|
|
124
|
+
create: ((_c = this.accountability) === null || _c === void 0 ? void 0 : _c.admin) === true
|
|
125
|
+
? this.schema
|
|
126
|
+
: (0, reduce_schema_1.reduceSchema)(this.schema, ((_d = this.accountability) === null || _d === void 0 ? void 0 : _d.permissions) || null, ['create']),
|
|
127
|
+
update: ((_e = this.accountability) === null || _e === void 0 ? void 0 : _e.admin) === true
|
|
128
|
+
? this.schema
|
|
129
|
+
: (0, reduce_schema_1.reduceSchema)(this.schema, ((_f = this.accountability) === null || _f === void 0 ? void 0 : _f.permissions) || null, ['update']),
|
|
130
|
+
delete: ((_g = this.accountability) === null || _g === void 0 ? void 0 : _g.admin) === true
|
|
131
|
+
? this.schema
|
|
132
|
+
: (0, reduce_schema_1.reduceSchema)(this.schema, ((_h = this.accountability) === null || _h === void 0 ? void 0 : _h.permissions) || null, ['delete']),
|
|
124
133
|
};
|
|
125
134
|
const { ReadCollectionTypes } = getReadableTypes();
|
|
126
135
|
const { CreateCollectionTypes, UpdateCollectionTypes, DeleteCollectionTypes } = getWritableTypes();
|
|
@@ -1268,6 +1277,8 @@ class GraphQLService {
|
|
|
1268
1277
|
return new users_1.UsersService(opts);
|
|
1269
1278
|
case 'directus_webhooks':
|
|
1270
1279
|
return new webhooks_1.WebhooksService(opts);
|
|
1280
|
+
case 'directus_shares':
|
|
1281
|
+
return new shares_1.SharesService(opts);
|
|
1271
1282
|
default:
|
|
1272
1283
|
return new items_1.ItemsService(collection, opts);
|
|
1273
1284
|
}
|
package/dist/services/index.d.ts
CHANGED
package/dist/services/index.js
CHANGED
package/dist/services/items.d.ts
CHANGED
|
@@ -1,25 +1,11 @@
|
|
|
1
1
|
import { Knex } from 'knex';
|
|
2
2
|
import Keyv from 'keyv';
|
|
3
3
|
import { Accountability, Query, PermissionsAction } from '@directus/shared/types';
|
|
4
|
-
import { AbstractService, AbstractServiceOptions, Item as AnyItem, PrimaryKey, SchemaOverview } from '../types';
|
|
4
|
+
import { AbstractService, AbstractServiceOptions, Item as AnyItem, PrimaryKey, SchemaOverview, MutationOptions } from '../types';
|
|
5
5
|
export declare type QueryOptions = {
|
|
6
6
|
stripNonRequested?: boolean;
|
|
7
7
|
permissionsAction?: PermissionsAction;
|
|
8
8
|
};
|
|
9
|
-
export declare type MutationOptions = {
|
|
10
|
-
/**
|
|
11
|
-
* Callback function that's fired whenever a revision is made in the mutation
|
|
12
|
-
*/
|
|
13
|
-
onRevisionCreate?: (pk: PrimaryKey) => void;
|
|
14
|
-
/**
|
|
15
|
-
* Flag to disable the auto purging of the cache. Is ignored when CACHE_AUTO_PURGE isn't enabled.
|
|
16
|
-
*/
|
|
17
|
-
autoPurgeCache?: false;
|
|
18
|
-
/**
|
|
19
|
-
* Allow disabling the emitting of hooks. Useful if a custom hook is fired (like files.upload)
|
|
20
|
-
*/
|
|
21
|
-
emitEvents?: boolean;
|
|
22
|
-
};
|
|
23
9
|
export declare class ItemsService<Item extends AnyItem = AnyItem> implements AbstractService {
|
|
24
10
|
collection: string;
|
|
25
11
|
knex: Knex;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { UsersService, MailService } from '.';
|
|
2
|
-
import { AbstractServiceOptions, PrimaryKey } from '../types';
|
|
3
|
-
import { ItemsService
|
|
2
|
+
import { AbstractServiceOptions, PrimaryKey, MutationOptions } from '../types';
|
|
3
|
+
import { ItemsService } from './items';
|
|
4
4
|
import { Notification } from '@directus/shared/types';
|
|
5
5
|
export declare class NotificationsService extends ItemsService {
|
|
6
6
|
usersService: UsersService;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ItemsService, QueryOptions
|
|
2
|
-
import { AbstractServiceOptions, Item, PrimaryKey } from '../types';
|
|
1
|
+
import { ItemsService, QueryOptions } from '../services/items';
|
|
2
|
+
import { AbstractServiceOptions, Item, PrimaryKey, MutationOptions } from '../types';
|
|
3
3
|
import { Query, PermissionsAction } from '@directus/shared/types';
|
|
4
4
|
import Keyv from 'keyv';
|
|
5
5
|
export declare class PermissionsService extends ItemsService {
|
package/dist/services/roles.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { AbstractServiceOptions, PrimaryKey } from '../types';
|
|
1
|
+
import { AbstractServiceOptions, MutationOptions, PrimaryKey } from '../types';
|
|
2
2
|
import { Query } from '@directus/shared/types';
|
|
3
|
-
import { ItemsService
|
|
3
|
+
import { ItemsService } from './items';
|
|
4
4
|
export declare class RolesService extends ItemsService {
|
|
5
5
|
constructor(options: AbstractServiceOptions);
|
|
6
6
|
private checkForOtherAdminRoles;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { AbstractServiceOptions, LoginResult, Item, PrimaryKey, MutationOptions } from '../types';
|
|
2
|
+
import { ItemsService } from './items';
|
|
3
|
+
import { AuthorizationService } from './authorization';
|
|
4
|
+
export declare class SharesService extends ItemsService {
|
|
5
|
+
authorizationService: AuthorizationService;
|
|
6
|
+
constructor(options: AbstractServiceOptions);
|
|
7
|
+
createOne(data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey>;
|
|
8
|
+
login(payload: Record<string, any>): Promise<LoginResult>;
|
|
9
|
+
/**
|
|
10
|
+
* Send a link to the given share ID to the given email(s). Note: you can only send a link to a share
|
|
11
|
+
* if you have read access to that particular share
|
|
12
|
+
*/
|
|
13
|
+
invite(payload: {
|
|
14
|
+
emails: string[];
|
|
15
|
+
share: PrimaryKey;
|
|
16
|
+
}): Promise<void>;
|
|
17
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
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.SharesService = void 0;
|
|
7
|
+
const items_1 = require("./items");
|
|
8
|
+
const argon2_1 = __importDefault(require("argon2"));
|
|
9
|
+
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
|
|
10
|
+
const ms_1 = __importDefault(require("ms"));
|
|
11
|
+
const exceptions_1 = require("../exceptions");
|
|
12
|
+
const env_1 = __importDefault(require("../env"));
|
|
13
|
+
const nanoid_1 = require("nanoid");
|
|
14
|
+
const authorization_1 = require("./authorization");
|
|
15
|
+
const users_1 = require("./users");
|
|
16
|
+
const mail_1 = require("./mail");
|
|
17
|
+
const user_name_1 = require("../utils/user-name");
|
|
18
|
+
const md_1 = require("../utils/md");
|
|
19
|
+
class SharesService extends items_1.ItemsService {
|
|
20
|
+
constructor(options) {
|
|
21
|
+
super('directus_shares', options);
|
|
22
|
+
this.authorizationService = new authorization_1.AuthorizationService({
|
|
23
|
+
accountability: this.accountability,
|
|
24
|
+
knex: this.knex,
|
|
25
|
+
schema: this.schema,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
async createOne(data, opts) {
|
|
29
|
+
await this.authorizationService.checkAccess('share', data.collection, data.item);
|
|
30
|
+
return super.createOne(data, opts);
|
|
31
|
+
}
|
|
32
|
+
async login(payload) {
|
|
33
|
+
var _a, _b;
|
|
34
|
+
const record = await this.knex
|
|
35
|
+
.select({
|
|
36
|
+
share_id: 'id',
|
|
37
|
+
share_role: 'role',
|
|
38
|
+
share_item: 'item',
|
|
39
|
+
share_collection: 'collection',
|
|
40
|
+
share_start: 'date_start',
|
|
41
|
+
share_end: 'date_end',
|
|
42
|
+
share_times_used: 'times_used',
|
|
43
|
+
share_max_uses: 'max_uses',
|
|
44
|
+
share_password: 'password',
|
|
45
|
+
})
|
|
46
|
+
.from('directus_shares')
|
|
47
|
+
.where('id', payload.share)
|
|
48
|
+
.andWhere((subQuery) => {
|
|
49
|
+
subQuery.whereNull('date_end').orWhere('date_end', '>=', this.knex.fn.now());
|
|
50
|
+
})
|
|
51
|
+
.andWhere((subQuery) => {
|
|
52
|
+
subQuery.whereNull('date_start').orWhere('date_start', '<=', this.knex.fn.now());
|
|
53
|
+
})
|
|
54
|
+
.andWhere((subQuery) => {
|
|
55
|
+
subQuery.whereNull('max_uses').orWhere('max_uses', '>=', this.knex.ref('times_used'));
|
|
56
|
+
})
|
|
57
|
+
.first();
|
|
58
|
+
if (!record) {
|
|
59
|
+
throw new exceptions_1.InvalidCredentialsException();
|
|
60
|
+
}
|
|
61
|
+
if (record.share_password && !(await argon2_1.default.verify(record.share_password, payload.password))) {
|
|
62
|
+
throw new exceptions_1.InvalidCredentialsException();
|
|
63
|
+
}
|
|
64
|
+
await this.knex('directus_shares')
|
|
65
|
+
.update({ times_used: record.share_times_used + 1 })
|
|
66
|
+
.where('id', record.share_id);
|
|
67
|
+
const tokenPayload = {
|
|
68
|
+
app_access: false,
|
|
69
|
+
admin_access: false,
|
|
70
|
+
role: record.share_role,
|
|
71
|
+
share: record.share_id,
|
|
72
|
+
share_scope: {
|
|
73
|
+
item: record.share_item,
|
|
74
|
+
collection: record.share_collection,
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
const accessToken = jsonwebtoken_1.default.sign(tokenPayload, env_1.default.SECRET, {
|
|
78
|
+
expiresIn: env_1.default.ACCESS_TOKEN_TTL,
|
|
79
|
+
issuer: 'directus',
|
|
80
|
+
});
|
|
81
|
+
const refreshToken = (0, nanoid_1.nanoid)(64);
|
|
82
|
+
const refreshTokenExpiration = new Date(Date.now() + (0, ms_1.default)(env_1.default.REFRESH_TOKEN_TTL));
|
|
83
|
+
await this.knex('directus_sessions').insert({
|
|
84
|
+
token: refreshToken,
|
|
85
|
+
expires: refreshTokenExpiration,
|
|
86
|
+
ip: (_a = this.accountability) === null || _a === void 0 ? void 0 : _a.ip,
|
|
87
|
+
user_agent: (_b = this.accountability) === null || _b === void 0 ? void 0 : _b.userAgent,
|
|
88
|
+
share: record.share_id,
|
|
89
|
+
});
|
|
90
|
+
await this.knex('directus_sessions').delete().where('expires', '<', new Date());
|
|
91
|
+
return {
|
|
92
|
+
accessToken,
|
|
93
|
+
refreshToken,
|
|
94
|
+
expires: (0, ms_1.default)(env_1.default.ACCESS_TOKEN_TTL),
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Send a link to the given share ID to the given email(s). Note: you can only send a link to a share
|
|
99
|
+
* if you have read access to that particular share
|
|
100
|
+
*/
|
|
101
|
+
async invite(payload) {
|
|
102
|
+
var _a;
|
|
103
|
+
if (!((_a = this.accountability) === null || _a === void 0 ? void 0 : _a.user))
|
|
104
|
+
throw new exceptions_1.ForbiddenException();
|
|
105
|
+
const share = await this.readOne(payload.share, { fields: ['collection'] });
|
|
106
|
+
const usersService = new users_1.UsersService({
|
|
107
|
+
knex: this.knex,
|
|
108
|
+
schema: this.schema,
|
|
109
|
+
});
|
|
110
|
+
const mailService = new mail_1.MailService({ schema: this.schema, accountability: this.accountability });
|
|
111
|
+
const userInfo = await usersService.readOne(this.accountability.user, {
|
|
112
|
+
fields: ['first_name', 'last_name', 'email', 'id'],
|
|
113
|
+
});
|
|
114
|
+
const message = `
|
|
115
|
+
Hello!
|
|
116
|
+
|
|
117
|
+
${(0, user_name_1.userName)(userInfo)} has invited you to view an item in ${share.collection}.
|
|
118
|
+
|
|
119
|
+
[Open](${env_1.default.PUBLIC_URL}/admin/shared/${payload.share})
|
|
120
|
+
`;
|
|
121
|
+
for (const email of payload.emails) {
|
|
122
|
+
await mailService.send({
|
|
123
|
+
template: {
|
|
124
|
+
name: 'base',
|
|
125
|
+
data: {
|
|
126
|
+
html: (0, md_1.md)(message),
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
to: email,
|
|
130
|
+
subject: `${(0, user_name_1.userName)(userInfo)} has shared an item with you`,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
exports.SharesService = SharesService;
|
|
@@ -442,7 +442,7 @@ class OASSpecsService {
|
|
|
442
442
|
],
|
|
443
443
|
};
|
|
444
444
|
}
|
|
445
|
-
else if (relationType === '
|
|
445
|
+
else if (relationType === 'a2o') {
|
|
446
446
|
const relatedTags = tags.filter((tag) => relation.meta.one_allowed_collections.includes(tag['x-collection']));
|
|
447
447
|
propertyObject.type = 'array';
|
|
448
448
|
propertyObject.items = {
|
package/dist/services/users.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Knex } from 'knex';
|
|
2
|
-
import { AbstractServiceOptions, Item, PrimaryKey, SchemaOverview } from '../types';
|
|
2
|
+
import { AbstractServiceOptions, Item, PrimaryKey, SchemaOverview, MutationOptions } from '../types';
|
|
3
3
|
import { Query } from '@directus/shared/types';
|
|
4
4
|
import { Accountability } from '@directus/shared/types';
|
|
5
|
-
import { ItemsService
|
|
5
|
+
import { ItemsService } from './items';
|
|
6
6
|
export declare class UsersService extends ItemsService {
|
|
7
7
|
knex: Knex;
|
|
8
8
|
accountability: Accountability | null;
|
package/dist/services/users.js
CHANGED
|
@@ -19,6 +19,7 @@ const mail_1 = require("./mail");
|
|
|
19
19
|
const settings_1 = require("./settings");
|
|
20
20
|
const stall_1 = require("../utils/stall");
|
|
21
21
|
const perf_hooks_1 = require("perf_hooks");
|
|
22
|
+
const utils_2 = require("@directus/shared/utils");
|
|
22
23
|
class UsersService extends items_1.ItemsService {
|
|
23
24
|
constructor(options) {
|
|
24
25
|
super('directus_users', options);
|
|
@@ -251,7 +252,7 @@ class UsersService extends items_1.ItemsService {
|
|
|
251
252
|
}
|
|
252
253
|
const STALL_TIME = 500;
|
|
253
254
|
const timeStart = perf_hooks_1.performance.now();
|
|
254
|
-
const user = await this.knex.select('status').from('directus_users').where({ email }).first();
|
|
255
|
+
const user = await this.knex.select('status', 'password').from('directus_users').where({ email }).first();
|
|
255
256
|
if ((user === null || user === void 0 ? void 0 : user.status) !== 'active') {
|
|
256
257
|
await (0, stall_1.stall)(STALL_TIME, timeStart);
|
|
257
258
|
throw new exceptions_2.ForbiddenException();
|
|
@@ -261,7 +262,7 @@ class UsersService extends items_1.ItemsService {
|
|
|
261
262
|
knex: this.knex,
|
|
262
263
|
accountability: this.accountability,
|
|
263
264
|
});
|
|
264
|
-
const payload = { email, scope: 'password-reset' };
|
|
265
|
+
const payload = { email, scope: 'password-reset', hash: (0, utils_2.getSimpleHash)('' + user.password) };
|
|
265
266
|
const token = jsonwebtoken_1.default.sign(payload, env_1.default.SECRET, { expiresIn: '1d', issuer: 'directus' });
|
|
266
267
|
const acceptURL = url ? `${url}?token=${token}` : `${env_1.default.PUBLIC_URL}/admin/reset-password?token=${token}`;
|
|
267
268
|
const subjectLine = subject ? subject : 'Password Reset Request';
|
|
@@ -279,11 +280,12 @@ class UsersService extends items_1.ItemsService {
|
|
|
279
280
|
await (0, stall_1.stall)(STALL_TIME, timeStart);
|
|
280
281
|
}
|
|
281
282
|
async resetPassword(token, password) {
|
|
282
|
-
const { email, scope } = jsonwebtoken_1.default.verify(token, env_1.default.SECRET, { issuer: 'directus' });
|
|
283
|
-
if (scope !== 'password-reset')
|
|
283
|
+
const { email, scope, hash } = jsonwebtoken_1.default.verify(token, env_1.default.SECRET, { issuer: 'directus' });
|
|
284
|
+
if (scope !== 'password-reset' || !hash)
|
|
284
285
|
throw new exceptions_2.ForbiddenException();
|
|
285
|
-
|
|
286
|
-
|
|
286
|
+
await this.checkPasswordPolicy([password]);
|
|
287
|
+
const user = await this.knex.select('id', 'status', 'password').from('directus_users').where({ email }).first();
|
|
288
|
+
if ((user === null || user === void 0 ? void 0 : user.status) !== 'active' || hash !== (0, utils_2.getSimpleHash)('' + user.password)) {
|
|
287
289
|
throw new exceptions_2.ForbiddenException();
|
|
288
290
|
}
|
|
289
291
|
// Allow unauthenticated update
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { AbstractServiceOptions, Item, PrimaryKey, Webhook } from '../types';
|
|
2
|
-
import { ItemsService
|
|
1
|
+
import { AbstractServiceOptions, Item, PrimaryKey, Webhook, MutationOptions } from '../types';
|
|
2
|
+
import { ItemsService } from './items';
|
|
3
3
|
export declare class WebhooksService extends ItemsService<Webhook> {
|
|
4
4
|
constructor(options: AbstractServiceOptions);
|
|
5
5
|
createOne(data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,29 @@
|
|
|
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
|
+
const knex_1 = __importDefault(require("knex"));
|
|
7
|
+
const knex_mock_client_1 = require("knex-mock-client");
|
|
8
|
+
const run_1 = __importDefault(require("../../../database/migrations/run"));
|
|
9
|
+
describe('run', () => {
|
|
10
|
+
let db;
|
|
11
|
+
let tracker;
|
|
12
|
+
beforeAll(() => {
|
|
13
|
+
db = (0, knex_1.default)({ client: knex_mock_client_1.MockClient });
|
|
14
|
+
tracker = (0, knex_mock_client_1.getTracker)();
|
|
15
|
+
});
|
|
16
|
+
afterEach(() => {
|
|
17
|
+
tracker.reset();
|
|
18
|
+
});
|
|
19
|
+
describe('when passed the argument up', () => {
|
|
20
|
+
it('returns "Nothing To Updage" if no directus_migrations', async () => {
|
|
21
|
+
// note the difference between an empty array and ['Empty']
|
|
22
|
+
tracker.on.select('directus_migrations').response(['Empty']);
|
|
23
|
+
await (0, run_1.default)(db, 'up').catch((e) => {
|
|
24
|
+
expect(e).toBeInstanceOf(Error);
|
|
25
|
+
expect(e.message).toBe('Nothing to upgrade');
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
});
|
package/dist/types/ast.d.ts
CHANGED
|
@@ -10,8 +10,8 @@ export declare type M2ONode = {
|
|
|
10
10
|
parentKey: string;
|
|
11
11
|
relatedKey: string;
|
|
12
12
|
};
|
|
13
|
-
export declare type
|
|
14
|
-
type: '
|
|
13
|
+
export declare type A2MNode = {
|
|
14
|
+
type: 'a2o';
|
|
15
15
|
names: string[];
|
|
16
16
|
children: {
|
|
17
17
|
[collection: string]: (NestedCollectionNode | FieldNode)[];
|
|
@@ -36,7 +36,7 @@ export declare type O2MNode = {
|
|
|
36
36
|
parentKey: string;
|
|
37
37
|
relatedKey: string;
|
|
38
38
|
};
|
|
39
|
-
export declare type NestedCollectionNode = M2ONode | O2MNode |
|
|
39
|
+
export declare type NestedCollectionNode = M2ONode | O2MNode | A2MNode;
|
|
40
40
|
export declare type FieldNode = {
|
|
41
41
|
type: 'field';
|
|
42
42
|
name: string;
|
package/dist/types/auth.d.ts
CHANGED
|
@@ -15,11 +15,42 @@ export interface User {
|
|
|
15
15
|
provider: string;
|
|
16
16
|
external_identifier: string | null;
|
|
17
17
|
auth_data: string | Record<string, unknown> | null;
|
|
18
|
+
app_access: boolean;
|
|
19
|
+
admin_access: boolean;
|
|
18
20
|
}
|
|
19
21
|
export declare type AuthData = Record<string, any> | null;
|
|
20
22
|
export interface Session {
|
|
21
23
|
token: string;
|
|
22
24
|
expires: Date;
|
|
23
25
|
data: string | Record<string, unknown> | null;
|
|
26
|
+
share: string;
|
|
24
27
|
}
|
|
25
28
|
export declare type SessionData = Record<string, any> | null;
|
|
29
|
+
export declare type DirectusTokenPayload = {
|
|
30
|
+
id?: string;
|
|
31
|
+
role: string | null;
|
|
32
|
+
app_access: boolean | number;
|
|
33
|
+
admin_access: boolean | number;
|
|
34
|
+
share?: string;
|
|
35
|
+
share_scope?: {
|
|
36
|
+
collection: string;
|
|
37
|
+
item: string;
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
export declare type ShareData = {
|
|
41
|
+
share_id: string;
|
|
42
|
+
share_role: string;
|
|
43
|
+
share_item: string;
|
|
44
|
+
share_collection: string;
|
|
45
|
+
share_start: Date;
|
|
46
|
+
share_end: Date;
|
|
47
|
+
share_times_used: number;
|
|
48
|
+
share_max_uses?: number;
|
|
49
|
+
share_password?: string;
|
|
50
|
+
};
|
|
51
|
+
export declare type LoginResult = {
|
|
52
|
+
accessToken: any;
|
|
53
|
+
refreshToken: any;
|
|
54
|
+
expires: any;
|
|
55
|
+
id?: any;
|
|
56
|
+
};
|
|
@@ -5,6 +5,7 @@ import { Logger } from 'pino';
|
|
|
5
5
|
import env from '../env';
|
|
6
6
|
import * as exceptions from '../exceptions';
|
|
7
7
|
import * as services from '../services';
|
|
8
|
+
import { Emitter } from '../emitter';
|
|
8
9
|
import { getSchema } from '../utils/get-schema';
|
|
9
10
|
import { SchemaOverview } from './schema';
|
|
10
11
|
export declare type ExtensionContext = {
|
|
@@ -12,6 +13,7 @@ export declare type ExtensionContext = {
|
|
|
12
13
|
exceptions: typeof exceptions;
|
|
13
14
|
database: Knex;
|
|
14
15
|
env: typeof env;
|
|
16
|
+
emitter: Emitter;
|
|
15
17
|
logger: Logger;
|
|
16
18
|
getSchema: typeof getSchema;
|
|
17
19
|
};
|
package/dist/types/items.d.ts
CHANGED
|
@@ -13,3 +13,17 @@ export declare type Alterations = {
|
|
|
13
13
|
}[];
|
|
14
14
|
delete: (number | string)[];
|
|
15
15
|
};
|
|
16
|
+
export declare type MutationOptions = {
|
|
17
|
+
/**
|
|
18
|
+
* Callback function that's fired whenever a revision is made in the mutation
|
|
19
|
+
*/
|
|
20
|
+
onRevisionCreate?: (pk: PrimaryKey) => void;
|
|
21
|
+
/**
|
|
22
|
+
* Flag to disable the auto purging of the cache. Is ignored when CACHE_AUTO_PURGE isn't enabled.
|
|
23
|
+
*/
|
|
24
|
+
autoPurgeCache?: false;
|
|
25
|
+
/**
|
|
26
|
+
* Allow disabling the emitting of hooks. Useful if a custom hook is fired (like files.upload)
|
|
27
|
+
*/
|
|
28
|
+
emitEvents?: boolean;
|
|
29
|
+
};
|
|
@@ -5,44 +5,6 @@ import { Aggregate, Filter, Query } from '@directus/shared/types';
|
|
|
5
5
|
* Apply the Query to a given Knex query builder instance
|
|
6
6
|
*/
|
|
7
7
|
export default function applyQuery(knex: Knex, collection: string, dbQuery: Knex.QueryBuilder, query: Query, schema: SchemaOverview, subQuery?: boolean): Knex.QueryBuilder;
|
|
8
|
-
/**
|
|
9
|
-
* Apply a given filter object to the Knex QueryBuilder instance.
|
|
10
|
-
*
|
|
11
|
-
* Relational nested filters, like the following example:
|
|
12
|
-
*
|
|
13
|
-
* ```json
|
|
14
|
-
* // Fetch pages that have articles written by Rijk
|
|
15
|
-
*
|
|
16
|
-
* {
|
|
17
|
-
* "articles": {
|
|
18
|
-
* "author": {
|
|
19
|
-
* "name": {
|
|
20
|
-
* "_eq": "Rijk"
|
|
21
|
-
* }
|
|
22
|
-
* }
|
|
23
|
-
* }
|
|
24
|
-
* }
|
|
25
|
-
* ```
|
|
26
|
-
*
|
|
27
|
-
* are handled by joining the nested tables, and using a where statement on the top level on the
|
|
28
|
-
* nested field through the join. This allows us to filter the top level items based on nested data.
|
|
29
|
-
* The where on the root is done with a subquery to prevent duplicates, any nested joins are done
|
|
30
|
-
* with aliases to prevent naming conflicts.
|
|
31
|
-
*
|
|
32
|
-
* The output SQL for the above would look something like:
|
|
33
|
-
*
|
|
34
|
-
* ```sql
|
|
35
|
-
* SELECT *
|
|
36
|
-
* FROM pages
|
|
37
|
-
* WHERE
|
|
38
|
-
* pages.id in (
|
|
39
|
-
* SELECT articles.page_id AS page_id
|
|
40
|
-
* FROM articles
|
|
41
|
-
* LEFT JOIN authors AS xviqp ON articles.author = xviqp.id
|
|
42
|
-
* WHERE xviqp.name = 'Rijk'
|
|
43
|
-
* )
|
|
44
|
-
* ```
|
|
45
|
-
*/
|
|
46
8
|
export declare function applyFilter(knex: Knex, schema: SchemaOverview, rootQuery: Knex.QueryBuilder, rootFilter: Filter, collection: string, subQuery?: boolean): Knex.QueryBuilder<any, any>;
|
|
47
9
|
export declare function applySearch(schema: SchemaOverview, dbQuery: Knex.QueryBuilder, searchQuery: string, collection: string): Promise<void>;
|
|
48
10
|
export declare function applyAggregate(dbQuery: Knex.QueryBuilder, aggregate: Aggregate, collection: string): void;
|