directus 9.0.0-rc.98 → 9.1.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 +25 -19
- package/dist/auth/auth.d.ts +4 -3
- package/dist/auth/auth.js +5 -3
- package/dist/auth/drivers/index.d.ts +3 -0
- package/dist/auth/drivers/index.js +3 -0
- package/dist/auth/drivers/ldap.d.ts +21 -0
- package/dist/auth/drivers/ldap.js +322 -0
- package/dist/auth/drivers/local.d.ts +3 -9
- package/dist/auth/drivers/local.js +4 -12
- package/dist/auth/drivers/oauth2.d.ts +19 -0
- package/dist/auth/drivers/oauth2.js +247 -0
- package/dist/auth/drivers/openid.d.ts +19 -0
- package/dist/auth/drivers/openid.js +256 -0
- package/dist/auth.d.ts +1 -0
- package/dist/auth.js +21 -15
- package/dist/cache.d.ts +1 -1
- package/dist/cache.js +7 -7
- package/dist/cli/commands/init/index.js +1 -1
- package/dist/cli/index.js +3 -3
- package/dist/cli/index.test.js +10 -5
- package/dist/cli/utils/create-env/env-stub.liquid +2 -2
- package/dist/constants.js +1 -1
- package/dist/controllers/auth.js +10 -140
- package/dist/controllers/extensions.js +2 -0
- package/dist/controllers/files.js +19 -8
- package/dist/controllers/not-found.js +8 -2
- package/dist/controllers/notifications.d.ts +2 -0
- package/dist/controllers/notifications.js +147 -0
- package/dist/controllers/utils.js +11 -1
- package/dist/database/helpers/date.d.ts +8 -0
- package/dist/database/helpers/date.js +44 -0
- package/dist/database/helpers/geometry.d.ts +4 -2
- package/dist/database/helpers/geometry.js +48 -27
- package/dist/database/index.d.ts +1 -1
- package/dist/database/index.js +15 -21
- package/dist/database/migrations/20211009A-add-auth-data.d.ts +3 -0
- package/dist/database/migrations/20211009A-add-auth-data.js +15 -0
- package/dist/database/migrations/20211016A-add-webhook-headers.d.ts +3 -0
- package/dist/database/migrations/20211016A-add-webhook-headers.js +15 -0
- package/dist/database/migrations/20211103A-set-unique-to-user-token.d.ts +3 -0
- package/dist/database/migrations/20211103A-set-unique-to-user-token.js +15 -0
- package/dist/database/migrations/20211103B-update-special-geometry.d.ts +3 -0
- package/dist/database/migrations/20211103B-update-special-geometry.js +25 -0
- package/dist/database/migrations/20211104A-remove-collections-listing.d.ts +3 -0
- package/dist/database/migrations/20211104A-remove-collections-listing.js +15 -0
- package/dist/database/migrations/20211118A-add-notifications.d.ts +3 -0
- package/dist/database/migrations/20211118A-add-notifications.js +28 -0
- package/dist/database/migrations/run.d.ts +1 -1
- package/dist/database/migrations/run.js +10 -4
- package/dist/database/run-ast.js +39 -55
- package/dist/database/seeds/run.js +4 -2
- package/dist/database/system-data/app-access-permissions/app-access-permissions.yaml +14 -0
- package/dist/database/system-data/collections/collections.yaml +2 -0
- package/dist/database/system-data/fields/activity.yaml +0 -2
- package/dist/database/system-data/fields/files.yaml +0 -1
- package/dist/database/system-data/fields/notifications.yaml +12 -0
- package/dist/database/system-data/fields/roles.yaml +0 -53
- package/dist/database/system-data/fields/settings.yaml +65 -69
- package/dist/database/system-data/fields/users.yaml +8 -3
- package/dist/database/system-data/fields/webhooks.yaml +32 -13
- package/dist/database/system-data/relations/relations.yaml +6 -0
- package/dist/emitter.d.ts +18 -8
- package/dist/emitter.js +68 -23
- package/dist/env.js +2 -2
- package/dist/exceptions/database/translate.js +26 -5
- package/dist/extensions.js +50 -27
- package/dist/index.d.ts +3 -0
- package/dist/index.js +20 -0
- package/dist/mailer.js +12 -3
- package/dist/middleware/authenticate.js +48 -48
- package/dist/middleware/error-handler.js +10 -3
- package/dist/middleware/get-permissions.d.ts +3 -0
- package/dist/middleware/get-permissions.js +15 -0
- package/dist/server.js +17 -7
- package/dist/services/activity.d.ts +7 -5
- package/dist/services/activity.js +82 -3
- package/dist/services/authentication.js +67 -58
- package/dist/services/authorization.d.ts +1 -1
- package/dist/services/authorization.js +13 -13
- package/dist/services/collections.d.ts +1 -1
- package/dist/services/collections.js +25 -23
- package/dist/services/fields.d.ts +1 -1
- package/dist/services/fields.js +29 -37
- package/dist/services/files.js +10 -11
- package/dist/services/graphql.js +13 -12
- package/dist/services/import.js +4 -4
- package/dist/services/index.d.ts +1 -0
- package/dist/services/index.js +1 -0
- package/dist/services/items.js +43 -83
- package/dist/services/mail/index.js +2 -2
- package/dist/services/mail/templates/base.liquid +153 -85
- package/dist/services/mail/templates/password-reset.liquid +3 -2
- package/dist/services/mail/templates/user-invitation.liquid +4 -4
- package/dist/services/meta.js +7 -8
- package/dist/services/notifications.d.ts +12 -0
- package/dist/services/notifications.js +41 -0
- package/dist/services/payload.d.ts +1 -0
- package/dist/services/payload.js +26 -11
- package/dist/services/permissions.d.ts +13 -1
- package/dist/services/permissions.js +56 -2
- package/dist/services/relations.d.ts +1 -1
- package/dist/services/relations.js +12 -18
- package/dist/services/specifications.js +21 -2
- package/dist/services/users.js +24 -23
- package/dist/services/utils.js +3 -3
- package/dist/services/webhooks.d.ts +2 -2
- package/dist/types/auth.d.ts +10 -2
- package/dist/types/collection.d.ts +1 -0
- package/dist/types/extensions.d.ts +18 -2
- package/dist/types/schema.d.ts +2 -2
- package/dist/types/webhooks.d.ts +7 -2
- package/dist/utils/apply-query.d.ts +3 -3
- package/dist/utils/apply-query.js +54 -16
- package/dist/utils/apply-snapshot.js +2 -2
- package/dist/utils/get-ast-from-query.js +6 -6
- package/dist/utils/get-auth-providers.d.ts +1 -0
- package/dist/utils/get-auth-providers.js +1 -0
- package/dist/utils/get-default-index-name.js +5 -6
- package/dist/utils/get-default-value.js +1 -1
- package/dist/utils/get-local-type.d.ts +2 -7
- package/dist/utils/get-local-type.js +106 -112
- package/dist/utils/get-permissions.d.ts +3 -0
- package/dist/utils/get-permissions.js +106 -0
- package/dist/utils/get-schema.js +11 -51
- package/dist/utils/get-simple-hash.d.ts +5 -0
- package/dist/utils/get-simple-hash.js +15 -0
- package/dist/utils/md.d.ts +4 -0
- package/dist/utils/md.js +15 -0
- package/dist/utils/reduce-schema.d.ts +2 -2
- package/dist/utils/reduce-schema.js +7 -10
- package/dist/utils/sanitize-query.js +3 -3
- package/dist/utils/track.js +2 -2
- package/dist/utils/user-name.d.ts +2 -0
- package/dist/utils/user-name.js +16 -0
- package/dist/utils/validate-query.js +4 -0
- package/dist/webhooks.js +18 -9
- package/example.env +15 -9
- package/package.json +41 -31
- package/dist/grant.d.ts +0 -5
- package/dist/grant.js +0 -24
- package/dist/middleware/session.d.ts +0 -3
- package/dist/middleware/session.js +0 -29
- package/dist/utils/geometry.d.ts +0 -2
- package/dist/utils/geometry.js +0 -20
- package/dist/utils/get-email-from-profile.d.ts +0 -9
- package/dist/utils/get-email-from-profile.js +0 -34
- package/index.js +0 -5
package/dist/app.js
CHANGED
|
@@ -40,6 +40,7 @@ const graphql_1 = __importDefault(require("./controllers/graphql"));
|
|
|
40
40
|
const items_1 = __importDefault(require("./controllers/items"));
|
|
41
41
|
const not_found_1 = __importDefault(require("./controllers/not-found"));
|
|
42
42
|
const panels_1 = __importDefault(require("./controllers/panels"));
|
|
43
|
+
const notifications_1 = __importDefault(require("./controllers/notifications"));
|
|
43
44
|
const permissions_1 = __importDefault(require("./controllers/permissions"));
|
|
44
45
|
const presets_1 = __importDefault(require("./controllers/presets"));
|
|
45
46
|
const relations_1 = __importDefault(require("./controllers/relations"));
|
|
@@ -51,12 +52,13 @@ const users_1 = __importDefault(require("./controllers/users"));
|
|
|
51
52
|
const utils_1 = __importDefault(require("./controllers/utils"));
|
|
52
53
|
const webhooks_1 = __importDefault(require("./controllers/webhooks"));
|
|
53
54
|
const database_1 = require("./database");
|
|
54
|
-
const emitter_1 = require("./emitter");
|
|
55
|
+
const emitter_1 = __importDefault(require("./emitter"));
|
|
55
56
|
const env_1 = __importDefault(require("./env"));
|
|
56
57
|
const exceptions_1 = require("./exceptions");
|
|
57
58
|
const extensions_2 = require("./extensions");
|
|
58
59
|
const logger_1 = __importStar(require("./logger"));
|
|
59
60
|
const authenticate_1 = __importDefault(require("./middleware/authenticate"));
|
|
61
|
+
const get_permissions_1 = __importDefault(require("./middleware/get-permissions"));
|
|
60
62
|
const cache_1 = __importDefault(require("./middleware/cache"));
|
|
61
63
|
const check_ip_1 = require("./middleware/check-ip");
|
|
62
64
|
const cors_1 = __importDefault(require("./middleware/cors"));
|
|
@@ -69,8 +71,8 @@ const track_1 = require("./utils/track");
|
|
|
69
71
|
const validate_env_1 = require("./utils/validate-env");
|
|
70
72
|
const validate_storage_1 = require("./utils/validate-storage");
|
|
71
73
|
const webhooks_2 = require("./webhooks");
|
|
72
|
-
const session_1 = require("./middleware/session");
|
|
73
74
|
const cache_2 = require("./cache");
|
|
75
|
+
const auth_2 = require("./auth");
|
|
74
76
|
const url_1 = require("./utils/url");
|
|
75
77
|
async function createApp() {
|
|
76
78
|
(0, validate_env_1.validateEnv)(['KEY', 'SECRET']);
|
|
@@ -88,14 +90,15 @@ async function createApp() {
|
|
|
88
90
|
logger_1.default.warn(`Database migrations have not all been run`);
|
|
89
91
|
}
|
|
90
92
|
await (0, cache_2.flushCaches)();
|
|
93
|
+
await (0, auth_2.registerAuthProviders)();
|
|
91
94
|
const extensionManager = (0, extensions_2.getExtensionManager)();
|
|
92
95
|
await extensionManager.initialize();
|
|
93
96
|
const app = (0, express_1.default)();
|
|
94
97
|
app.disable('x-powered-by');
|
|
95
98
|
app.set('trust proxy', true);
|
|
96
99
|
app.set('query parser', (str) => qs_1.default.parse(str, { depth: 10 }));
|
|
97
|
-
await
|
|
98
|
-
await
|
|
100
|
+
await emitter_1.default.emitInit('app.before', { app });
|
|
101
|
+
await emitter_1.default.emitInit('middlewares.before', { app });
|
|
99
102
|
app.use(logger_1.expressLogger);
|
|
100
103
|
app.use((req, res, next) => {
|
|
101
104
|
express_1.default.json({
|
|
@@ -125,30 +128,31 @@ async function createApp() {
|
|
|
125
128
|
}
|
|
126
129
|
});
|
|
127
130
|
if (env_1.default.SERVE_APP) {
|
|
128
|
-
const adminPath = require.resolve('@directus/app
|
|
131
|
+
const adminPath = require.resolve('@directus/app');
|
|
129
132
|
const adminUrl = new url_1.Url(env_1.default.PUBLIC_URL).addPath('admin');
|
|
130
133
|
// Set the App's base path according to the APIs public URL
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
+
const html = await fs_extra_1.default.readFile(adminPath, 'utf8');
|
|
135
|
+
const htmlWithBase = html.replace(/<base \/>/, `<base href="${adminUrl.toString({ rootRelative: true })}/" />`);
|
|
136
|
+
const noCacheIndexHtmlHandler = (req, res) => {
|
|
137
|
+
res.setHeader('Cache-Control', 'no-cache');
|
|
138
|
+
res.send(htmlWithBase);
|
|
139
|
+
};
|
|
140
|
+
app.get('/admin', noCacheIndexHtmlHandler);
|
|
134
141
|
app.use('/admin', express_1.default.static(path_1.default.join(adminPath, '..')));
|
|
135
|
-
app.use('/admin/*',
|
|
136
|
-
res.send(html);
|
|
137
|
-
});
|
|
142
|
+
app.use('/admin/*', noCacheIndexHtmlHandler);
|
|
138
143
|
}
|
|
139
144
|
// use the rate limiter - all routes for now
|
|
140
145
|
if (env_1.default.RATE_LIMITER_ENABLED === true) {
|
|
141
146
|
app.use(rate_limiter_1.default);
|
|
142
147
|
}
|
|
143
|
-
// We only rely on cookie-sessions in the oAuth flow where it's required
|
|
144
|
-
app.use(session_1.session);
|
|
145
148
|
app.use(authenticate_1.default);
|
|
146
149
|
app.use(check_ip_1.checkIP);
|
|
147
150
|
app.use(sanitize_query_1.default);
|
|
148
|
-
await (0, emitter_1.emitAsyncSafe)('middlewares.init.after', { app });
|
|
149
|
-
await (0, emitter_1.emitAsyncSafe)('routes.init.before', { app });
|
|
150
151
|
app.use(cache_1.default);
|
|
151
152
|
app.use(schema_1.default);
|
|
153
|
+
app.use(get_permissions_1.default);
|
|
154
|
+
await emitter_1.default.emitInit('middlewares.after', { app });
|
|
155
|
+
await emitter_1.default.emitInit('routes.before', { app });
|
|
152
156
|
app.use('/auth', auth_1.default);
|
|
153
157
|
app.use('/graphql', graphql_1.default);
|
|
154
158
|
app.use('/activity', activity_1.default);
|
|
@@ -160,6 +164,7 @@ async function createApp() {
|
|
|
160
164
|
app.use('/files', files_1.default);
|
|
161
165
|
app.use('/folders', folders_1.default);
|
|
162
166
|
app.use('/items', items_1.default);
|
|
167
|
+
app.use('/notifications', notifications_1.default);
|
|
163
168
|
app.use('/panels', panels_1.default);
|
|
164
169
|
app.use('/permissions', permissions_1.default);
|
|
165
170
|
app.use('/presets', presets_1.default);
|
|
@@ -171,16 +176,17 @@ async function createApp() {
|
|
|
171
176
|
app.use('/users', users_1.default);
|
|
172
177
|
app.use('/utils', utils_1.default);
|
|
173
178
|
app.use('/webhooks', webhooks_1.default);
|
|
174
|
-
|
|
179
|
+
// Register custom endpoints
|
|
180
|
+
await emitter_1.default.emitInit('routes.custom.before', { app });
|
|
175
181
|
app.use(extensionManager.getEndpointRouter());
|
|
176
|
-
await
|
|
182
|
+
await emitter_1.default.emitInit('routes.custom.after', { app });
|
|
177
183
|
app.use(not_found_1.default);
|
|
178
184
|
app.use(error_handler_1.default);
|
|
179
|
-
await
|
|
185
|
+
await emitter_1.default.emitInit('routes.after', { app });
|
|
180
186
|
// Register all webhooks
|
|
181
187
|
await (0, webhooks_2.register)();
|
|
182
188
|
(0, track_1.track)('serverStarted');
|
|
183
|
-
|
|
189
|
+
await emitter_1.default.emitInit('app.after', { app });
|
|
184
190
|
return app;
|
|
185
191
|
}
|
|
186
192
|
exports.default = createApp;
|
package/dist/auth/auth.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { Knex } from 'knex';
|
|
2
|
-
import { User, SessionData } from '../types';
|
|
2
|
+
import { AuthDriverOptions, SchemaOverview, User, SessionData } from '../types';
|
|
3
3
|
export declare abstract class AuthDriver {
|
|
4
4
|
knex: Knex;
|
|
5
|
-
|
|
5
|
+
schema: SchemaOverview;
|
|
6
|
+
constructor(options: AuthDriverOptions, _config: Record<string, any>);
|
|
6
7
|
/**
|
|
7
8
|
* Get user id for a given provider payload
|
|
8
9
|
*
|
|
@@ -35,7 +36,7 @@ export declare abstract class AuthDriver {
|
|
|
35
36
|
* @param _sessionData Session data
|
|
36
37
|
* @throws InvalidCredentialsException
|
|
37
38
|
*/
|
|
38
|
-
refresh(_user: User,
|
|
39
|
+
refresh(_user: User, sessionData: SessionData): Promise<SessionData>;
|
|
39
40
|
/**
|
|
40
41
|
* Handle user session termination
|
|
41
42
|
*
|
package/dist/auth/auth.js
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.AuthDriver = void 0;
|
|
4
4
|
class AuthDriver {
|
|
5
|
-
constructor(
|
|
6
|
-
this.knex = knex;
|
|
5
|
+
constructor(options, _config) {
|
|
6
|
+
this.knex = options.knex;
|
|
7
|
+
this.schema = options.schema;
|
|
7
8
|
}
|
|
8
9
|
/**
|
|
9
10
|
* Check with the (external) provider if the user is allowed entry to Directus
|
|
@@ -24,8 +25,9 @@ class AuthDriver {
|
|
|
24
25
|
* @param _sessionData Session data
|
|
25
26
|
* @throws InvalidCredentialsException
|
|
26
27
|
*/
|
|
27
|
-
async refresh(_user,
|
|
28
|
+
async refresh(_user, sessionData) {
|
|
28
29
|
/* Optional */
|
|
30
|
+
return sessionData;
|
|
29
31
|
}
|
|
30
32
|
/**
|
|
31
33
|
* Handle user session termination
|
|
@@ -11,3 +11,6 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
11
11
|
};
|
|
12
12
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
13
|
__exportStar(require("./local"), exports);
|
|
14
|
+
__exportStar(require("./oauth2"), exports);
|
|
15
|
+
__exportStar(require("./openid"), exports);
|
|
16
|
+
__exportStar(require("./ldap"), exports);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
import { Client } from 'ldapjs';
|
|
3
|
+
import { AuthDriver } from '../auth';
|
|
4
|
+
import { AuthDriverOptions, User, SessionData } from '../../types';
|
|
5
|
+
import { UsersService } from '../../services';
|
|
6
|
+
export declare class LDAPAuthDriver extends AuthDriver {
|
|
7
|
+
bindClient: Client;
|
|
8
|
+
usersService: UsersService;
|
|
9
|
+
config: Record<string, any>;
|
|
10
|
+
constructor(options: AuthDriverOptions, config: Record<string, any>);
|
|
11
|
+
private validateBindClient;
|
|
12
|
+
private fetchUserDn;
|
|
13
|
+
private fetchUserInfo;
|
|
14
|
+
private fetchUserGroups;
|
|
15
|
+
private fetchUserId;
|
|
16
|
+
getUserID(payload: Record<string, any>): Promise<string>;
|
|
17
|
+
verify(user: User, password?: string): Promise<void>;
|
|
18
|
+
login(user: User, payload: Record<string, any>): Promise<SessionData>;
|
|
19
|
+
refresh(user: User): Promise<SessionData>;
|
|
20
|
+
}
|
|
21
|
+
export declare function createLDAPAuthRouter(provider: string): Router;
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
|
5
|
+
}) : (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
o[k2] = m[k];
|
|
8
|
+
}));
|
|
9
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
10
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
11
|
+
}) : function(o, v) {
|
|
12
|
+
o["default"] = v;
|
|
13
|
+
});
|
|
14
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
15
|
+
if (mod && mod.__esModule) return mod;
|
|
16
|
+
var result = {};
|
|
17
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
18
|
+
__setModuleDefault(result, mod);
|
|
19
|
+
return result;
|
|
20
|
+
};
|
|
21
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
22
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
23
|
+
};
|
|
24
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
+
exports.createLDAPAuthRouter = exports.LDAPAuthDriver = void 0;
|
|
26
|
+
const express_1 = require("express");
|
|
27
|
+
const ldapjs_1 = __importStar(require("ldapjs"));
|
|
28
|
+
const ms_1 = __importDefault(require("ms"));
|
|
29
|
+
const joi_1 = __importDefault(require("joi"));
|
|
30
|
+
const auth_1 = require("../auth");
|
|
31
|
+
const exceptions_1 = require("../../exceptions");
|
|
32
|
+
const services_1 = require("../../services");
|
|
33
|
+
const async_handler_1 = __importDefault(require("../../utils/async-handler"));
|
|
34
|
+
const env_1 = __importDefault(require("../../env"));
|
|
35
|
+
const respond_1 = require("../../middleware/respond");
|
|
36
|
+
const logger_1 = __importDefault(require("../../logger"));
|
|
37
|
+
// 0x2: ACCOUNTDISABLE
|
|
38
|
+
// 0x10: LOCKOUT
|
|
39
|
+
// 0x800000: PASSWORD_EXPIRED
|
|
40
|
+
const INVALID_ACCOUNT_FLAGS = 0x800012;
|
|
41
|
+
class LDAPAuthDriver extends auth_1.AuthDriver {
|
|
42
|
+
constructor(options, config) {
|
|
43
|
+
var _a;
|
|
44
|
+
super(options, config);
|
|
45
|
+
const { bindDn, bindPassword, userDn, provider, clientUrl } = config;
|
|
46
|
+
if (!bindDn || !bindPassword || !userDn || !provider || (!clientUrl && !((_a = config.client) === null || _a === void 0 ? void 0 : _a.socketPath))) {
|
|
47
|
+
throw new exceptions_1.InvalidConfigException('Invalid provider config', { provider });
|
|
48
|
+
}
|
|
49
|
+
const clientConfig = typeof config.client === 'object' ? config.client : {};
|
|
50
|
+
this.bindClient = ldapjs_1.default.createClient({ url: clientUrl, reconnect: true, ...clientConfig });
|
|
51
|
+
this.bindClient.on('error', (err) => {
|
|
52
|
+
logger_1.default.warn(err);
|
|
53
|
+
});
|
|
54
|
+
this.usersService = new services_1.UsersService({ knex: this.knex, schema: this.schema });
|
|
55
|
+
this.config = config;
|
|
56
|
+
}
|
|
57
|
+
async validateBindClient() {
|
|
58
|
+
const { bindDn, bindPassword, provider } = this.config;
|
|
59
|
+
return new Promise((resolve, reject) => {
|
|
60
|
+
// Healthcheck bind user
|
|
61
|
+
this.bindClient.search(bindDn, {}, (err, res) => {
|
|
62
|
+
if (err) {
|
|
63
|
+
reject(handleError(err));
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
res.on('searchEntry', () => {
|
|
67
|
+
resolve();
|
|
68
|
+
});
|
|
69
|
+
res.on('error', (err) => {
|
|
70
|
+
if (!(err instanceof ldapjs_1.OperationsError)) {
|
|
71
|
+
reject(handleError(err));
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
// Rebind on OperationsError
|
|
75
|
+
this.bindClient.bind(bindDn, bindPassword, (err) => {
|
|
76
|
+
if (err) {
|
|
77
|
+
const error = handleError(err);
|
|
78
|
+
if (error instanceof exceptions_1.InvalidCredentialsException) {
|
|
79
|
+
reject(new exceptions_1.InvalidConfigException('Invalid bind user', { provider }));
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
reject(error);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
resolve();
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
async fetchUserDn(identifier) {
|
|
94
|
+
const { userDn, userAttribute, userScope } = this.config;
|
|
95
|
+
return new Promise((resolve, reject) => {
|
|
96
|
+
// Search for the user in LDAP by attribute
|
|
97
|
+
this.bindClient.search(userDn, { filter: `(${userAttribute !== null && userAttribute !== void 0 ? userAttribute : 'cn'}=${identifier})`, scope: userScope !== null && userScope !== void 0 ? userScope : 'one' }, (err, res) => {
|
|
98
|
+
if (err) {
|
|
99
|
+
reject(handleError(err));
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
res.on('searchEntry', ({ object }) => {
|
|
103
|
+
resolve(object.dn.toLowerCase());
|
|
104
|
+
});
|
|
105
|
+
res.on('error', (err) => {
|
|
106
|
+
reject(handleError(err));
|
|
107
|
+
});
|
|
108
|
+
res.on('end', () => {
|
|
109
|
+
resolve(undefined);
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
async fetchUserInfo(userDn) {
|
|
115
|
+
const { mailAttribute } = this.config;
|
|
116
|
+
return new Promise((resolve, reject) => {
|
|
117
|
+
// Fetch user info in LDAP by domain component
|
|
118
|
+
this.bindClient.search(userDn, { attributes: ['givenName', 'sn', mailAttribute !== null && mailAttribute !== void 0 ? mailAttribute : 'mail', 'userAccountControl'] }, (err, res) => {
|
|
119
|
+
if (err) {
|
|
120
|
+
reject(handleError(err));
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
res.on('searchEntry', ({ object }) => {
|
|
124
|
+
const email = object[mailAttribute !== null && mailAttribute !== void 0 ? mailAttribute : 'mail'];
|
|
125
|
+
const user = {
|
|
126
|
+
firstName: typeof object.givenName === 'object' ? object.givenName[0] : object.givenName,
|
|
127
|
+
lastName: typeof object.sn === 'object' ? object.sn[0] : object.sn,
|
|
128
|
+
email: typeof email === 'object' ? email[0] : email,
|
|
129
|
+
userAccountControl: typeof object.userAccountControl === 'object'
|
|
130
|
+
? Number(object.userAccountControl[0])
|
|
131
|
+
: Number(object.userAccountControl),
|
|
132
|
+
};
|
|
133
|
+
resolve(user);
|
|
134
|
+
});
|
|
135
|
+
res.on('error', (err) => {
|
|
136
|
+
reject(handleError(err));
|
|
137
|
+
});
|
|
138
|
+
res.on('end', () => {
|
|
139
|
+
resolve(undefined);
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
async fetchUserGroups(userDn) {
|
|
145
|
+
const { groupDn, groupAttribute, groupScope } = this.config;
|
|
146
|
+
if (!groupDn) {
|
|
147
|
+
return Promise.resolve([]);
|
|
148
|
+
}
|
|
149
|
+
return new Promise((resolve, reject) => {
|
|
150
|
+
let userGroups = [];
|
|
151
|
+
// Search for the user info in LDAP by group attribute
|
|
152
|
+
this.bindClient.search(groupDn, {
|
|
153
|
+
attributes: ['cn'],
|
|
154
|
+
filter: `(${groupAttribute !== null && groupAttribute !== void 0 ? groupAttribute : 'member'}=${userDn})`,
|
|
155
|
+
scope: groupScope !== null && groupScope !== void 0 ? groupScope : 'one',
|
|
156
|
+
}, (err, res) => {
|
|
157
|
+
if (err) {
|
|
158
|
+
reject(handleError(err));
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
res.on('searchEntry', ({ object }) => {
|
|
162
|
+
if (typeof object.cn === 'object') {
|
|
163
|
+
userGroups = [...userGroups, ...object.cn];
|
|
164
|
+
}
|
|
165
|
+
else if (object.cn) {
|
|
166
|
+
userGroups.push(object.cn);
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
res.on('error', (err) => {
|
|
170
|
+
reject(handleError(err));
|
|
171
|
+
});
|
|
172
|
+
res.on('end', () => {
|
|
173
|
+
resolve(userGroups);
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
async fetchUserId(userDn) {
|
|
179
|
+
const user = await this.knex
|
|
180
|
+
.select('id')
|
|
181
|
+
.from('directus_users')
|
|
182
|
+
.orWhereRaw('LOWER(??) = ?', ['external_identifier', userDn.toLowerCase()])
|
|
183
|
+
.first();
|
|
184
|
+
return user === null || user === void 0 ? void 0 : user.id;
|
|
185
|
+
}
|
|
186
|
+
async getUserID(payload) {
|
|
187
|
+
var _a;
|
|
188
|
+
if (!payload.identifier) {
|
|
189
|
+
throw new exceptions_1.InvalidCredentialsException();
|
|
190
|
+
}
|
|
191
|
+
await this.validateBindClient();
|
|
192
|
+
const userDn = await this.fetchUserDn(payload.identifier);
|
|
193
|
+
if (!userDn) {
|
|
194
|
+
throw new exceptions_1.InvalidCredentialsException();
|
|
195
|
+
}
|
|
196
|
+
const userId = await this.fetchUserId(userDn);
|
|
197
|
+
const userGroups = await this.fetchUserGroups(userDn);
|
|
198
|
+
let userRole;
|
|
199
|
+
if (userGroups.length) {
|
|
200
|
+
userRole = await this.knex
|
|
201
|
+
.select('id')
|
|
202
|
+
.from('directus_roles')
|
|
203
|
+
.whereRaw(`LOWER(??) IN (${userGroups.map(() => '?')})`, [
|
|
204
|
+
'name',
|
|
205
|
+
...userGroups.map((group) => group.toLowerCase()),
|
|
206
|
+
])
|
|
207
|
+
.first();
|
|
208
|
+
}
|
|
209
|
+
if (userId) {
|
|
210
|
+
await this.usersService.updateOne(userId, { role: (_a = userRole === null || userRole === void 0 ? void 0 : userRole.id) !== null && _a !== void 0 ? _a : null });
|
|
211
|
+
return userId;
|
|
212
|
+
}
|
|
213
|
+
const userInfo = await this.fetchUserInfo(userDn);
|
|
214
|
+
if (!userInfo) {
|
|
215
|
+
throw new exceptions_1.InvalidCredentialsException();
|
|
216
|
+
}
|
|
217
|
+
await this.usersService.createOne({
|
|
218
|
+
provider: this.config.provider,
|
|
219
|
+
first_name: userInfo.firstName,
|
|
220
|
+
last_name: userInfo.lastName,
|
|
221
|
+
email: userInfo.email,
|
|
222
|
+
external_identifier: userDn,
|
|
223
|
+
role: userRole === null || userRole === void 0 ? void 0 : userRole.id,
|
|
224
|
+
});
|
|
225
|
+
return (await this.fetchUserId(userDn));
|
|
226
|
+
}
|
|
227
|
+
async verify(user, password) {
|
|
228
|
+
if (!user.external_identifier || !password) {
|
|
229
|
+
throw new exceptions_1.InvalidCredentialsException();
|
|
230
|
+
}
|
|
231
|
+
return new Promise((resolve, reject) => {
|
|
232
|
+
const clientConfig = typeof this.config.client === 'object' ? this.config.client : {};
|
|
233
|
+
const client = ldapjs_1.default.createClient({
|
|
234
|
+
url: this.config.clientUrl,
|
|
235
|
+
...clientConfig,
|
|
236
|
+
reconnect: false,
|
|
237
|
+
});
|
|
238
|
+
client.on('error', (err) => {
|
|
239
|
+
reject(handleError(err));
|
|
240
|
+
});
|
|
241
|
+
client.bind(user.external_identifier, password, (err) => {
|
|
242
|
+
if (err) {
|
|
243
|
+
reject(handleError(err));
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
resolve();
|
|
247
|
+
}
|
|
248
|
+
client.destroy();
|
|
249
|
+
});
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
async login(user, payload) {
|
|
253
|
+
await this.verify(user, payload.password);
|
|
254
|
+
return null;
|
|
255
|
+
}
|
|
256
|
+
async refresh(user) {
|
|
257
|
+
await this.validateBindClient();
|
|
258
|
+
const userInfo = await this.fetchUserInfo(user.external_identifier);
|
|
259
|
+
if ((userInfo === null || userInfo === void 0 ? void 0 : userInfo.userAccountControl) && userInfo.userAccountControl & INVALID_ACCOUNT_FLAGS) {
|
|
260
|
+
throw new exceptions_1.InvalidCredentialsException();
|
|
261
|
+
}
|
|
262
|
+
return null;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
exports.LDAPAuthDriver = LDAPAuthDriver;
|
|
266
|
+
const handleError = (e) => {
|
|
267
|
+
if (e instanceof ldapjs_1.InappropriateAuthenticationError ||
|
|
268
|
+
e instanceof ldapjs_1.InvalidCredentialsError ||
|
|
269
|
+
e instanceof ldapjs_1.InsufficientAccessRightsError) {
|
|
270
|
+
return new exceptions_1.InvalidCredentialsException();
|
|
271
|
+
}
|
|
272
|
+
return new exceptions_1.ServiceUnavailableException('Service returned unexpected error', {
|
|
273
|
+
service: 'ldap',
|
|
274
|
+
message: e.message,
|
|
275
|
+
});
|
|
276
|
+
};
|
|
277
|
+
function createLDAPAuthRouter(provider) {
|
|
278
|
+
const router = (0, express_1.Router)();
|
|
279
|
+
const loginSchema = joi_1.default.object({
|
|
280
|
+
identifier: joi_1.default.string().required(),
|
|
281
|
+
password: joi_1.default.string().required(),
|
|
282
|
+
mode: joi_1.default.string().valid('cookie', 'json'),
|
|
283
|
+
otp: joi_1.default.string(),
|
|
284
|
+
}).unknown();
|
|
285
|
+
router.post('/', (0, async_handler_1.default)(async (req, res, next) => {
|
|
286
|
+
var _a, _b;
|
|
287
|
+
const accountability = {
|
|
288
|
+
ip: req.ip,
|
|
289
|
+
userAgent: req.get('user-agent'),
|
|
290
|
+
role: null,
|
|
291
|
+
};
|
|
292
|
+
const authenticationService = new services_1.AuthenticationService({
|
|
293
|
+
accountability: accountability,
|
|
294
|
+
schema: req.schema,
|
|
295
|
+
});
|
|
296
|
+
const { error } = loginSchema.validate(req.body);
|
|
297
|
+
if (error) {
|
|
298
|
+
throw new exceptions_1.InvalidPayloadException(error.message);
|
|
299
|
+
}
|
|
300
|
+
const mode = req.body.mode || 'json';
|
|
301
|
+
const { accessToken, refreshToken, expires } = await authenticationService.login(provider, req.body, (_a = req.body) === null || _a === void 0 ? void 0 : _a.otp);
|
|
302
|
+
const payload = {
|
|
303
|
+
data: { access_token: accessToken, expires },
|
|
304
|
+
};
|
|
305
|
+
if (mode === 'json') {
|
|
306
|
+
payload.data.refresh_token = refreshToken;
|
|
307
|
+
}
|
|
308
|
+
if (mode === 'cookie') {
|
|
309
|
+
res.cookie(env_1.default.REFRESH_TOKEN_COOKIE_NAME, refreshToken, {
|
|
310
|
+
httpOnly: true,
|
|
311
|
+
domain: env_1.default.REFRESH_TOKEN_COOKIE_DOMAIN,
|
|
312
|
+
maxAge: (0, ms_1.default)(env_1.default.REFRESH_TOKEN_TTL),
|
|
313
|
+
secure: (_b = env_1.default.REFRESH_TOKEN_COOKIE_SECURE) !== null && _b !== void 0 ? _b : false,
|
|
314
|
+
sameSite: env_1.default.REFRESH_TOKEN_COOKIE_SAME_SITE || 'strict',
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
res.locals.payload = payload;
|
|
318
|
+
return next();
|
|
319
|
+
}), respond_1.respond);
|
|
320
|
+
return router;
|
|
321
|
+
}
|
|
322
|
+
exports.createLDAPAuthRouter = createLDAPAuthRouter;
|
|
@@ -1,15 +1,9 @@
|
|
|
1
|
-
import { AuthDriver } from '../auth';
|
|
2
|
-
import { User } from '../../types';
|
|
3
1
|
import { Router } from 'express';
|
|
2
|
+
import { AuthDriver } from '../auth';
|
|
3
|
+
import { User, SessionData } from '../../types';
|
|
4
4
|
export declare class LocalAuthDriver extends AuthDriver {
|
|
5
|
-
/**
|
|
6
|
-
* Get user id by email
|
|
7
|
-
*/
|
|
8
5
|
getUserID(payload: Record<string, any>): Promise<string>;
|
|
9
|
-
/**
|
|
10
|
-
* Verify user password
|
|
11
|
-
*/
|
|
12
6
|
verify(user: User, password?: string): Promise<void>;
|
|
13
|
-
login(user: User, payload: Record<string, any>): Promise<
|
|
7
|
+
login(user: User, payload: Record<string, any>): Promise<SessionData>;
|
|
14
8
|
}
|
|
15
9
|
export declare function createLocalAuthRouter(provider: string): Router;
|
|
@@ -4,20 +4,17 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.createLocalAuthRouter = exports.LocalAuthDriver = void 0;
|
|
7
|
+
const express_1 = require("express");
|
|
7
8
|
const argon2_1 = __importDefault(require("argon2"));
|
|
9
|
+
const ms_1 = __importDefault(require("ms"));
|
|
10
|
+
const joi_1 = __importDefault(require("joi"));
|
|
8
11
|
const auth_1 = require("../auth");
|
|
9
12
|
const exceptions_1 = require("../../exceptions");
|
|
10
13
|
const services_1 = require("../../services");
|
|
11
|
-
const express_1 = require("express");
|
|
12
|
-
const joi_1 = __importDefault(require("joi"));
|
|
13
14
|
const async_handler_1 = __importDefault(require("../../utils/async-handler"));
|
|
14
15
|
const env_1 = __importDefault(require("../../env"));
|
|
15
|
-
const ms_1 = __importDefault(require("ms"));
|
|
16
16
|
const respond_1 = require("../../middleware/respond");
|
|
17
17
|
class LocalAuthDriver extends auth_1.AuthDriver {
|
|
18
|
-
/**
|
|
19
|
-
* Get user id by email
|
|
20
|
-
*/
|
|
21
18
|
async getUserID(payload) {
|
|
22
19
|
if (!payload.email) {
|
|
23
20
|
throw new exceptions_1.InvalidCredentialsException();
|
|
@@ -32,18 +29,13 @@ class LocalAuthDriver extends auth_1.AuthDriver {
|
|
|
32
29
|
}
|
|
33
30
|
return user.id;
|
|
34
31
|
}
|
|
35
|
-
/**
|
|
36
|
-
* Verify user password
|
|
37
|
-
*/
|
|
38
32
|
async verify(user, password) {
|
|
39
33
|
if (!user.password || !(await argon2_1.default.verify(user.password, password))) {
|
|
40
34
|
throw new exceptions_1.InvalidCredentialsException();
|
|
41
35
|
}
|
|
42
36
|
}
|
|
43
37
|
async login(user, payload) {
|
|
44
|
-
|
|
45
|
-
await this.verify(user, payload.password);
|
|
46
|
-
}
|
|
38
|
+
await this.verify(user, payload.password);
|
|
47
39
|
return null;
|
|
48
40
|
}
|
|
49
41
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
import { Client } from 'openid-client';
|
|
3
|
+
import { LocalAuthDriver } from './local';
|
|
4
|
+
import { UsersService } from '../../services';
|
|
5
|
+
import { AuthDriverOptions, User, SessionData } from '../../types';
|
|
6
|
+
export declare class OAuth2AuthDriver extends LocalAuthDriver {
|
|
7
|
+
client: Client;
|
|
8
|
+
redirectUrl: string;
|
|
9
|
+
usersService: UsersService;
|
|
10
|
+
config: Record<string, any>;
|
|
11
|
+
constructor(options: AuthDriverOptions, config: Record<string, any>);
|
|
12
|
+
generateCodeVerifier(): string;
|
|
13
|
+
generateAuthUrl(codeVerifier: string): string;
|
|
14
|
+
private fetchUserId;
|
|
15
|
+
getUserID(payload: Record<string, any>): Promise<string>;
|
|
16
|
+
login(user: User): Promise<SessionData>;
|
|
17
|
+
refresh(user: User, sessionData: SessionData): Promise<SessionData>;
|
|
18
|
+
}
|
|
19
|
+
export declare function createOAuth2AuthRouter(providerName: string): Router;
|