directus 9.1.2 → 9.3.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/auth/drivers/ldap.js +11 -2
- package/dist/auth/drivers/oauth2.d.ts +1 -1
- package/dist/auth/drivers/oauth2.js +46 -19
- package/dist/auth/drivers/openid.d.ts +1 -1
- package/dist/auth/drivers/openid.js +34 -17
- package/dist/auth.js +5 -3
- package/dist/cli/commands/bootstrap/index.js +3 -2
- package/dist/cli/commands/database/install.js +2 -4
- package/dist/cli/commands/init/index.js +3 -7
- package/dist/cli/commands/schema/apply.js +26 -10
- package/dist/cli/utils/defaults.d.ts +11 -0
- package/dist/cli/utils/defaults.js +14 -0
- package/dist/controllers/assets.js +0 -27
- package/dist/controllers/auth.js +7 -2
- package/dist/controllers/extensions.js +1 -1
- package/dist/database/{functions/types.d.ts → helpers/date/dialects/mssql.d.ts} +2 -1
- package/dist/database/{functions → helpers/date}/dialects/mssql.js +4 -6
- package/dist/database/{functions → helpers/date}/dialects/mysql.d.ts +2 -4
- package/dist/database/{functions → helpers/date}/dialects/mysql.js +4 -6
- package/dist/database/{functions/dialects/mssql.d.ts → helpers/date/dialects/oracle.d.ts} +2 -4
- package/dist/database/{functions → helpers/date}/dialects/oracle.js +4 -6
- package/dist/database/helpers/date/dialects/postgres.d.ts +12 -0
- package/dist/database/{functions → helpers/date}/dialects/postgres.js +4 -6
- package/dist/database/{functions → helpers/date}/dialects/sqlite.d.ts +3 -4
- package/dist/database/helpers/date/dialects/sqlite.js +35 -0
- package/dist/database/helpers/date/index.d.ts +6 -0
- package/dist/database/helpers/date/index.js +15 -0
- package/dist/database/helpers/date/types.d.ts +13 -0
- package/dist/database/helpers/date/types.js +10 -0
- package/dist/database/helpers/geometry/dialects/mssql.d.ts +14 -0
- package/dist/database/helpers/geometry/dialects/mssql.js +36 -0
- package/dist/database/helpers/geometry/dialects/mysql.d.ts +7 -0
- package/dist/database/helpers/geometry/dialects/mysql.js +16 -0
- package/dist/database/helpers/geometry/dialects/oracle.d.ts +15 -0
- package/dist/database/helpers/geometry/dialects/oracle.js +39 -0
- package/dist/database/helpers/geometry/dialects/postgres.d.ts +10 -0
- package/dist/database/helpers/geometry/dialects/postgres.js +23 -0
- package/dist/database/helpers/geometry/dialects/redshift.d.ts +7 -0
- package/dist/database/helpers/geometry/dialects/redshift.js +16 -0
- package/dist/database/helpers/geometry/dialects/sqlite.d.ts +6 -0
- package/dist/database/helpers/geometry/dialects/sqlite.js +14 -0
- package/dist/database/helpers/geometry/index.d.ts +6 -0
- package/dist/database/helpers/geometry/index.js +15 -0
- package/dist/database/helpers/{geometry.d.ts → geometry/types.d.ts} +3 -7
- package/dist/database/helpers/geometry/types.js +54 -0
- package/dist/database/helpers/index.d.ts +8 -0
- package/dist/database/helpers/index.js +33 -0
- package/dist/database/helpers/types.d.ts +5 -0
- package/dist/database/helpers/types.js +9 -0
- package/dist/database/index.js +6 -6
- package/dist/database/run-ast.js +5 -5
- package/dist/database/seeds/run.js +3 -3
- package/dist/database/system-data/fields/notifications.yaml +1 -0
- package/dist/emitter.d.ts +3 -2
- package/dist/emitter.js +13 -6
- package/dist/env.js +1 -0
- 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 +22 -3
- package/dist/middleware/sanitize-query.js +1 -1
- package/dist/services/activity.js +7 -2
- package/dist/services/assets.js +14 -0
- package/dist/services/fields.d.ts +2 -0
- package/dist/services/fields.js +57 -26
- package/dist/services/files.d.ts +1 -1
- package/dist/services/files.js +27 -19
- package/dist/services/graphql.js +3 -0
- package/dist/services/items.js +18 -29
- package/dist/services/payload.d.ts +2 -0
- package/dist/services/payload.js +3 -3
- package/dist/services/users.js +8 -6
- package/dist/tests/database/migrations/run.test.d.ts +1 -0
- package/dist/tests/database/migrations/run.test.js +29 -0
- package/dist/types/extensions.d.ts +2 -0
- package/dist/utils/apply-query.js +9 -12
- package/dist/utils/apply-snapshot.js +91 -37
- package/dist/utils/get-column.js +2 -2
- package/dist/utils/get-default-index-name.js +2 -2
- package/dist/utils/get-local-type.js +1 -12
- package/dist/utils/get-permissions.d.ts +2 -2
- package/dist/utils/get-permissions.js +103 -66
- package/dist/utils/sanitize-query.js +1 -12
- package/dist/utils/validate-query.js +1 -1
- package/dist/webhooks.js +16 -24
- package/package.json +15 -14
- package/dist/database/functions/dialects/oracle.d.ts +0 -14
- package/dist/database/functions/dialects/postgres.d.ts +0 -14
- package/dist/database/functions/dialects/sqlite.js +0 -33
- package/dist/database/functions/index.d.ts +0 -3
- package/dist/database/functions/index.js +0 -26
- package/dist/database/functions/types.js +0 -2
- package/dist/database/helpers/date.d.ts +0 -8
- package/dist/database/helpers/date.js +0 -44
- package/dist/database/helpers/geometry.js +0 -189
- package/dist/utils/get-simple-hash.d.ts +0 -5
- package/dist/utils/get-simple-hash.js +0 -15
|
@@ -87,6 +87,12 @@ class LDAPAuthDriver extends auth_1.AuthDriver {
|
|
|
87
87
|
}
|
|
88
88
|
});
|
|
89
89
|
});
|
|
90
|
+
res.on('end', (result) => {
|
|
91
|
+
if ((result === null || result === void 0 ? void 0 : result.status) === 0) {
|
|
92
|
+
// Handle edge case with IBM systems where authenticated bind user could not fetch their DN
|
|
93
|
+
reject(new exceptions_1.UnexpectedResponseException('Failed to find bind user record'));
|
|
94
|
+
}
|
|
95
|
+
});
|
|
90
96
|
});
|
|
91
97
|
});
|
|
92
98
|
}
|
|
@@ -94,7 +100,10 @@ class LDAPAuthDriver extends auth_1.AuthDriver {
|
|
|
94
100
|
const { userDn, userAttribute, userScope } = this.config;
|
|
95
101
|
return new Promise((resolve, reject) => {
|
|
96
102
|
// Search for the user in LDAP by attribute
|
|
97
|
-
this.bindClient.search(userDn, {
|
|
103
|
+
this.bindClient.search(userDn, {
|
|
104
|
+
filter: new ldapjs_1.EqualityFilter({ attribute: userAttribute !== null && userAttribute !== void 0 ? userAttribute : 'cn', value: identifier }),
|
|
105
|
+
scope: userScope !== null && userScope !== void 0 ? userScope : 'one',
|
|
106
|
+
}, (err, res) => {
|
|
98
107
|
if (err) {
|
|
99
108
|
reject(handleError(err));
|
|
100
109
|
return;
|
|
@@ -151,7 +160,7 @@ class LDAPAuthDriver extends auth_1.AuthDriver {
|
|
|
151
160
|
// Search for the user info in LDAP by group attribute
|
|
152
161
|
this.bindClient.search(groupDn, {
|
|
153
162
|
attributes: ['cn'],
|
|
154
|
-
filter:
|
|
163
|
+
filter: new ldapjs_1.EqualityFilter({ attribute: groupAttribute !== null && groupAttribute !== void 0 ? groupAttribute : 'member', value: userDn }),
|
|
155
164
|
scope: groupScope !== null && groupScope !== void 0 ? groupScope : 'one',
|
|
156
165
|
}, (err, res) => {
|
|
157
166
|
if (err) {
|
|
@@ -10,7 +10,7 @@ export declare class OAuth2AuthDriver extends LocalAuthDriver {
|
|
|
10
10
|
config: Record<string, any>;
|
|
11
11
|
constructor(options: AuthDriverOptions, config: Record<string, any>);
|
|
12
12
|
generateCodeVerifier(): string;
|
|
13
|
-
generateAuthUrl(codeVerifier: string): string;
|
|
13
|
+
generateAuthUrl(codeVerifier: string, prompt?: boolean): string;
|
|
14
14
|
private fetchUserId;
|
|
15
15
|
getUserID(payload: Record<string, any>): Promise<string>;
|
|
16
16
|
login(user: User): Promise<SessionData>;
|
|
@@ -32,7 +32,8 @@ class OAuth2AuthDriver extends local_1.LocalAuthDriver {
|
|
|
32
32
|
authorization_endpoint: authorizeUrl,
|
|
33
33
|
token_endpoint: accessUrl,
|
|
34
34
|
userinfo_endpoint: profileUrl,
|
|
35
|
-
|
|
35
|
+
// Required for openid providers (openid flow should be preferred!)
|
|
36
|
+
issuer: additionalConfig.issuerUrl,
|
|
36
37
|
});
|
|
37
38
|
this.client = new issuer.Client({
|
|
38
39
|
client_id: clientId,
|
|
@@ -44,17 +45,20 @@ class OAuth2AuthDriver extends local_1.LocalAuthDriver {
|
|
|
44
45
|
generateCodeVerifier() {
|
|
45
46
|
return openid_client_1.generators.codeVerifier();
|
|
46
47
|
}
|
|
47
|
-
generateAuthUrl(codeVerifier) {
|
|
48
|
+
generateAuthUrl(codeVerifier, prompt = false) {
|
|
48
49
|
var _a;
|
|
49
50
|
try {
|
|
50
51
|
const codeChallenge = openid_client_1.generators.codeChallenge(codeVerifier);
|
|
52
|
+
const paramsConfig = typeof this.config.params === 'object' ? this.config.params : {};
|
|
51
53
|
return this.client.authorizationUrl({
|
|
52
54
|
scope: (_a = this.config.scope) !== null && _a !== void 0 ? _a : 'email',
|
|
55
|
+
access_type: 'offline',
|
|
56
|
+
prompt: prompt ? 'consent' : undefined,
|
|
57
|
+
...paramsConfig,
|
|
53
58
|
code_challenge: codeChallenge,
|
|
54
59
|
code_challenge_method: 'S256',
|
|
55
60
|
// Some providers require state even with PKCE
|
|
56
61
|
state: codeChallenge,
|
|
57
|
-
access_type: 'offline',
|
|
58
62
|
});
|
|
59
63
|
}
|
|
60
64
|
catch (e) {
|
|
@@ -72,6 +76,7 @@ class OAuth2AuthDriver extends local_1.LocalAuthDriver {
|
|
|
72
76
|
async getUserID(payload) {
|
|
73
77
|
var _a;
|
|
74
78
|
if (!payload.code || !payload.codeVerifier) {
|
|
79
|
+
logger_1.default.trace('[OAuth2] No code or codeVerifier in payload');
|
|
75
80
|
throw new exceptions_1.InvalidCredentialsException();
|
|
76
81
|
}
|
|
77
82
|
let tokenSet;
|
|
@@ -112,6 +117,7 @@ class OAuth2AuthDriver extends local_1.LocalAuthDriver {
|
|
|
112
117
|
}
|
|
113
118
|
// Is public registration allowed?
|
|
114
119
|
if (!allowPublicRegistration) {
|
|
120
|
+
logger_1.default.trace(`[OAuth2] User doesn't exist, and public registration not allowed for provider "${this.config.provider}"`);
|
|
115
121
|
throw new exceptions_1.InvalidCredentialsException();
|
|
116
122
|
}
|
|
117
123
|
await this.usersService.createOne({
|
|
@@ -136,35 +142,44 @@ class OAuth2AuthDriver extends local_1.LocalAuthDriver {
|
|
|
136
142
|
logger_1.default.warn(`Session data isn't valid JSON: ${authData}`);
|
|
137
143
|
}
|
|
138
144
|
}
|
|
139
|
-
if (
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
145
|
+
if (authData === null || authData === void 0 ? void 0 : authData.refreshToken) {
|
|
146
|
+
try {
|
|
147
|
+
const tokenSet = await this.client.refresh(authData.refreshToken);
|
|
148
|
+
// Update user refreshToken if provided
|
|
149
|
+
if (tokenSet.refresh_token) {
|
|
150
|
+
await this.usersService.updateOne(user.id, {
|
|
151
|
+
auth_data: JSON.stringify({ refreshToken: tokenSet.refresh_token }),
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
catch (e) {
|
|
156
|
+
throw handleError(e);
|
|
157
|
+
}
|
|
148
158
|
}
|
|
159
|
+
return sessionData;
|
|
149
160
|
}
|
|
150
161
|
}
|
|
151
162
|
exports.OAuth2AuthDriver = OAuth2AuthDriver;
|
|
152
163
|
const handleError = (e) => {
|
|
153
164
|
if (e instanceof openid_client_1.errors.OPError) {
|
|
154
165
|
if (e.error === 'invalid_grant') {
|
|
166
|
+
logger_1.default.trace(e, `[OAuth2] Invalid grant.`);
|
|
155
167
|
// Invalid token
|
|
156
|
-
return new exceptions_1.
|
|
168
|
+
return new exceptions_1.InvalidTokenException();
|
|
157
169
|
}
|
|
170
|
+
logger_1.default.trace(e, `[OAuth2] Unknown OP error.`);
|
|
158
171
|
// Server response error
|
|
159
172
|
return new exceptions_1.ServiceUnavailableException('Service returned unexpected response', {
|
|
160
|
-
service: '
|
|
173
|
+
service: 'oauth2',
|
|
161
174
|
message: e.error_description,
|
|
162
175
|
});
|
|
163
176
|
}
|
|
164
177
|
else if (e instanceof openid_client_1.errors.RPError) {
|
|
165
178
|
// Internal client error
|
|
179
|
+
logger_1.default.trace(e, `[OAuth2] Unknown RP error.`);
|
|
166
180
|
return new exceptions_1.InvalidCredentialsException();
|
|
167
181
|
}
|
|
182
|
+
logger_1.default.trace(e, `[OAuth2] Unknown error.`);
|
|
168
183
|
return e;
|
|
169
184
|
};
|
|
170
185
|
function createOAuth2AuthRouter(providerName) {
|
|
@@ -172,7 +187,8 @@ function createOAuth2AuthRouter(providerName) {
|
|
|
172
187
|
router.get('/', (req, res) => {
|
|
173
188
|
const provider = (0, auth_1.getAuthProvider)(providerName);
|
|
174
189
|
const codeVerifier = provider.generateCodeVerifier();
|
|
175
|
-
const
|
|
190
|
+
const prompt = !!req.query.prompt;
|
|
191
|
+
const token = jsonwebtoken_1.default.sign({ verifier: codeVerifier, redirect: req.query.redirect, prompt }, env_1.default.SECRET, {
|
|
176
192
|
expiresIn: '5m',
|
|
177
193
|
issuer: 'directus',
|
|
178
194
|
});
|
|
@@ -180,7 +196,7 @@ function createOAuth2AuthRouter(providerName) {
|
|
|
180
196
|
httpOnly: true,
|
|
181
197
|
sameSite: 'lax',
|
|
182
198
|
});
|
|
183
|
-
return res.redirect(provider.generateAuthUrl(codeVerifier));
|
|
199
|
+
return res.redirect(provider.generateAuthUrl(codeVerifier, prompt));
|
|
184
200
|
}, respond_1.respond);
|
|
185
201
|
router.get('/callback', (0, async_handler_1.default)(async (req, res, next) => {
|
|
186
202
|
var _a;
|
|
@@ -189,9 +205,10 @@ function createOAuth2AuthRouter(providerName) {
|
|
|
189
205
|
tokenData = jsonwebtoken_1.default.verify(req.cookies[`oauth2.${providerName}`], env_1.default.SECRET, { issuer: 'directus' });
|
|
190
206
|
}
|
|
191
207
|
catch (e) {
|
|
208
|
+
logger_1.default.warn(e, `[OAuth2] Couldn't verify OAuth2 cookie`);
|
|
192
209
|
throw new exceptions_1.InvalidCredentialsException();
|
|
193
210
|
}
|
|
194
|
-
const { verifier, redirect } = tokenData;
|
|
211
|
+
const { verifier, redirect, prompt } = tokenData;
|
|
195
212
|
const authenticationService = new services_1.AuthenticationService({
|
|
196
213
|
accountability: {
|
|
197
214
|
ip: req.ip,
|
|
@@ -204,7 +221,7 @@ function createOAuth2AuthRouter(providerName) {
|
|
|
204
221
|
try {
|
|
205
222
|
res.clearCookie(`oauth2.${providerName}`);
|
|
206
223
|
if (!req.query.code || !req.query.state) {
|
|
207
|
-
logger_1.default.warn(`Couldn't extract OAuth2 code or state from query: ${JSON.stringify(req.query)}`);
|
|
224
|
+
logger_1.default.warn(`[OAuth2]Couldn't extract OAuth2 code or state from query: ${JSON.stringify(req.query)}`);
|
|
208
225
|
}
|
|
209
226
|
authResponse = await authenticationService.login(providerName, {
|
|
210
227
|
code: req.query.code,
|
|
@@ -213,7 +230,10 @@ function createOAuth2AuthRouter(providerName) {
|
|
|
213
230
|
});
|
|
214
231
|
}
|
|
215
232
|
catch (error) {
|
|
216
|
-
|
|
233
|
+
// Prompt user for a new refresh_token if invalidated
|
|
234
|
+
if (error instanceof exceptions_1.InvalidTokenException && !prompt) {
|
|
235
|
+
return res.redirect(`./?${redirect ? `redirect=${redirect}&` : ''}prompt=true`);
|
|
236
|
+
}
|
|
217
237
|
if (redirect) {
|
|
218
238
|
let reason = 'UNKNOWN_EXCEPTION';
|
|
219
239
|
if (error instanceof exceptions_1.ServiceUnavailableException) {
|
|
@@ -222,8 +242,15 @@ function createOAuth2AuthRouter(providerName) {
|
|
|
222
242
|
else if (error instanceof exceptions_1.InvalidCredentialsException) {
|
|
223
243
|
reason = 'INVALID_USER';
|
|
224
244
|
}
|
|
245
|
+
else if (error instanceof exceptions_1.InvalidTokenException) {
|
|
246
|
+
reason = 'INVALID_TOKEN';
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
logger_1.default.warn(error, `[OAuth2] Unexpected error during OAuth2 login`);
|
|
250
|
+
}
|
|
225
251
|
return res.redirect(`${redirect.split('?')[0]}?reason=${reason}`);
|
|
226
252
|
}
|
|
253
|
+
logger_1.default.warn(error, `[OAuth2] Unexpected error during OAuth2 login`);
|
|
227
254
|
throw error;
|
|
228
255
|
}
|
|
229
256
|
const { accessToken, refreshToken, expires } = authResponse;
|
|
@@ -10,7 +10,7 @@ export declare class OpenIDAuthDriver extends LocalAuthDriver {
|
|
|
10
10
|
config: Record<string, any>;
|
|
11
11
|
constructor(options: AuthDriverOptions, config: Record<string, any>);
|
|
12
12
|
generateCodeVerifier(): string;
|
|
13
|
-
generateAuthUrl(codeVerifier: string): Promise<string>;
|
|
13
|
+
generateAuthUrl(codeVerifier: string, prompt?: boolean): Promise<string>;
|
|
14
14
|
private fetchUserId;
|
|
15
15
|
getUserID(payload: Record<string, any>): Promise<string>;
|
|
16
16
|
login(user: User): Promise<SessionData>;
|
|
@@ -50,18 +50,21 @@ class OpenIDAuthDriver extends local_1.LocalAuthDriver {
|
|
|
50
50
|
generateCodeVerifier() {
|
|
51
51
|
return openid_client_1.generators.codeVerifier();
|
|
52
52
|
}
|
|
53
|
-
async generateAuthUrl(codeVerifier) {
|
|
53
|
+
async generateAuthUrl(codeVerifier, prompt = false) {
|
|
54
54
|
var _a;
|
|
55
55
|
try {
|
|
56
56
|
const client = await this.client;
|
|
57
57
|
const codeChallenge = openid_client_1.generators.codeChallenge(codeVerifier);
|
|
58
|
+
const paramsConfig = typeof this.config.params === 'object' ? this.config.params : {};
|
|
58
59
|
return client.authorizationUrl({
|
|
59
60
|
scope: (_a = this.config.scope) !== null && _a !== void 0 ? _a : 'openid profile email',
|
|
61
|
+
access_type: 'offline',
|
|
62
|
+
prompt: prompt ? 'consent' : undefined,
|
|
63
|
+
...paramsConfig,
|
|
60
64
|
code_challenge: codeChallenge,
|
|
61
65
|
code_challenge_method: 'S256',
|
|
62
66
|
// Some providers require state even with PKCE
|
|
63
67
|
state: codeChallenge,
|
|
64
|
-
access_type: 'offline',
|
|
65
68
|
});
|
|
66
69
|
}
|
|
67
70
|
catch (e) {
|
|
@@ -144,17 +147,22 @@ class OpenIDAuthDriver extends local_1.LocalAuthDriver {
|
|
|
144
147
|
logger_1.default.warn(`Session data isn't valid JSON: ${authData}`);
|
|
145
148
|
}
|
|
146
149
|
}
|
|
147
|
-
if (
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
150
|
+
if (authData === null || authData === void 0 ? void 0 : authData.refreshToken) {
|
|
151
|
+
try {
|
|
152
|
+
const client = await this.client;
|
|
153
|
+
const tokenSet = await client.refresh(authData.refreshToken);
|
|
154
|
+
// Update user refreshToken if provided
|
|
155
|
+
if (tokenSet.refresh_token) {
|
|
156
|
+
await this.usersService.updateOne(user.id, {
|
|
157
|
+
auth_data: JSON.stringify({ refreshToken: tokenSet.refresh_token }),
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
catch (e) {
|
|
162
|
+
throw handleError(e);
|
|
163
|
+
}
|
|
157
164
|
}
|
|
165
|
+
return sessionData;
|
|
158
166
|
}
|
|
159
167
|
}
|
|
160
168
|
exports.OpenIDAuthDriver = OpenIDAuthDriver;
|
|
@@ -162,7 +170,7 @@ const handleError = (e) => {
|
|
|
162
170
|
if (e instanceof openid_client_1.errors.OPError) {
|
|
163
171
|
if (e.error === 'invalid_grant') {
|
|
164
172
|
// Invalid token
|
|
165
|
-
return new exceptions_1.
|
|
173
|
+
return new exceptions_1.InvalidTokenException();
|
|
166
174
|
}
|
|
167
175
|
// Server response error
|
|
168
176
|
return new exceptions_1.ServiceUnavailableException('Service returned unexpected response', {
|
|
@@ -181,7 +189,8 @@ function createOpenIDAuthRouter(providerName) {
|
|
|
181
189
|
router.get('/', (0, async_handler_1.default)(async (req, res) => {
|
|
182
190
|
const provider = (0, auth_1.getAuthProvider)(providerName);
|
|
183
191
|
const codeVerifier = provider.generateCodeVerifier();
|
|
184
|
-
const
|
|
192
|
+
const prompt = !!req.query.prompt;
|
|
193
|
+
const token = jsonwebtoken_1.default.sign({ verifier: codeVerifier, redirect: req.query.redirect, prompt }, env_1.default.SECRET, {
|
|
185
194
|
expiresIn: '5m',
|
|
186
195
|
issuer: 'directus',
|
|
187
196
|
});
|
|
@@ -189,7 +198,7 @@ function createOpenIDAuthRouter(providerName) {
|
|
|
189
198
|
httpOnly: true,
|
|
190
199
|
sameSite: 'lax',
|
|
191
200
|
});
|
|
192
|
-
return res.redirect(await provider.generateAuthUrl(codeVerifier));
|
|
201
|
+
return res.redirect(await provider.generateAuthUrl(codeVerifier, prompt));
|
|
193
202
|
}), respond_1.respond);
|
|
194
203
|
router.get('/callback', (0, async_handler_1.default)(async (req, res, next) => {
|
|
195
204
|
var _a;
|
|
@@ -198,9 +207,10 @@ function createOpenIDAuthRouter(providerName) {
|
|
|
198
207
|
tokenData = jsonwebtoken_1.default.verify(req.cookies[`openid.${providerName}`], env_1.default.SECRET, { issuer: 'directus' });
|
|
199
208
|
}
|
|
200
209
|
catch (e) {
|
|
210
|
+
logger_1.default.warn(e, `[OpenID] Couldn't verify OpenID cookie`);
|
|
201
211
|
throw new exceptions_1.InvalidCredentialsException();
|
|
202
212
|
}
|
|
203
|
-
const { verifier, redirect } = tokenData;
|
|
213
|
+
const { verifier, redirect, prompt } = tokenData;
|
|
204
214
|
const authenticationService = new services_1.AuthenticationService({
|
|
205
215
|
accountability: {
|
|
206
216
|
ip: req.ip,
|
|
@@ -213,7 +223,7 @@ function createOpenIDAuthRouter(providerName) {
|
|
|
213
223
|
try {
|
|
214
224
|
res.clearCookie(`openid.${providerName}`);
|
|
215
225
|
if (!req.query.code || !req.query.state) {
|
|
216
|
-
logger_1.default.warn(`Couldn't extract OpenID code or state from query: ${JSON.stringify(req.query)}`);
|
|
226
|
+
logger_1.default.warn(`[OpenID] Couldn't extract OpenID code or state from query: ${JSON.stringify(req.query)}`);
|
|
217
227
|
}
|
|
218
228
|
authResponse = await authenticationService.login(providerName, {
|
|
219
229
|
code: req.query.code,
|
|
@@ -222,6 +232,10 @@ function createOpenIDAuthRouter(providerName) {
|
|
|
222
232
|
});
|
|
223
233
|
}
|
|
224
234
|
catch (error) {
|
|
235
|
+
// Prompt user for a new refresh_token if invalidated
|
|
236
|
+
if (error instanceof exceptions_1.InvalidTokenException && !prompt) {
|
|
237
|
+
return res.redirect(`./?${redirect ? `redirect=${redirect}&` : ''}prompt=true`);
|
|
238
|
+
}
|
|
225
239
|
logger_1.default.warn(error);
|
|
226
240
|
if (redirect) {
|
|
227
241
|
let reason = 'UNKNOWN_EXCEPTION';
|
|
@@ -231,6 +245,9 @@ function createOpenIDAuthRouter(providerName) {
|
|
|
231
245
|
else if (error instanceof exceptions_1.InvalidCredentialsException) {
|
|
232
246
|
reason = 'INVALID_USER';
|
|
233
247
|
}
|
|
248
|
+
else if (error instanceof exceptions_1.InvalidTokenException) {
|
|
249
|
+
reason = 'INVALID_TOKEN';
|
|
250
|
+
}
|
|
234
251
|
return res.redirect(`${redirect.split('?')[0]}?reason=${reason}`);
|
|
235
252
|
}
|
|
236
253
|
throw error;
|
package/dist/auth.js
CHANGED
|
@@ -24,9 +24,11 @@ function getAuthProvider(provider) {
|
|
|
24
24
|
exports.getAuthProvider = getAuthProvider;
|
|
25
25
|
async function registerAuthProviders() {
|
|
26
26
|
const options = { knex: (0, database_1.default)(), schema: await (0, get_schema_1.getSchema)() };
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
// Register default provider if not disabled
|
|
28
|
+
if (!env_1.default.AUTH_DISABLE_DEFAULT) {
|
|
29
|
+
const defaultProvider = getProviderInstance('local', options);
|
|
30
|
+
providers.set(constants_1.DEFAULT_AUTH_PROVIDER, defaultProvider);
|
|
31
|
+
}
|
|
30
32
|
if (!env_1.default.AUTH_PROVIDERS) {
|
|
31
33
|
return;
|
|
32
34
|
}
|
|
@@ -30,6 +30,7 @@ const logger_1 = __importDefault(require("../../../logger"));
|
|
|
30
30
|
const get_schema_1 = require("../../../utils/get-schema");
|
|
31
31
|
const services_1 = require("../../../services");
|
|
32
32
|
const database_1 = __importStar(require("../../../database"));
|
|
33
|
+
const defaults_1 = require("../../utils/defaults");
|
|
33
34
|
async function bootstrap({ skipAdminInit }) {
|
|
34
35
|
logger_1.default.info('Initializing bootstrap...');
|
|
35
36
|
const database = (0, database_1.default)();
|
|
@@ -75,7 +76,7 @@ async function waitForDatabase(database) {
|
|
|
75
76
|
async function createDefaultAdmin(schema) {
|
|
76
77
|
logger_1.default.info('Setting up first admin role...');
|
|
77
78
|
const rolesService = new services_1.RolesService({ schema });
|
|
78
|
-
const role = await rolesService.createOne(
|
|
79
|
+
const role = await rolesService.createOne(defaults_1.defaultAdminRole);
|
|
79
80
|
logger_1.default.info('Adding first admin user...');
|
|
80
81
|
const usersService = new services_1.UsersService({ schema });
|
|
81
82
|
let adminEmail = env_1.default.ADMIN_EMAIL;
|
|
@@ -88,5 +89,5 @@ async function createDefaultAdmin(schema) {
|
|
|
88
89
|
adminPassword = (0, nanoid_1.nanoid)(12);
|
|
89
90
|
logger_1.default.info(`No admin password provided. Defaulting to "${adminPassword}"`);
|
|
90
91
|
}
|
|
91
|
-
await usersService.createOne({ email: adminEmail, password: adminPassword, role });
|
|
92
|
+
await usersService.createOne({ email: adminEmail, password: adminPassword, role, ...defaults_1.defaultAdminUser });
|
|
92
93
|
}
|
|
@@ -3,15 +3,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const run_1 = __importDefault(require("../../../database/
|
|
7
|
-
const run_2 = __importDefault(require("../../../database/seeds/run"));
|
|
6
|
+
const run_1 = __importDefault(require("../../../database/seeds/run"));
|
|
8
7
|
const database_1 = __importDefault(require("../../../database"));
|
|
9
8
|
const logger_1 = __importDefault(require("../../../logger"));
|
|
10
9
|
async function start() {
|
|
11
10
|
const database = (0, database_1.default)();
|
|
12
11
|
try {
|
|
13
|
-
await (0,
|
|
14
|
-
await (0, run_1.default)(database, 'latest');
|
|
12
|
+
await (0, run_1.default)(database);
|
|
15
13
|
database.destroy();
|
|
16
14
|
process.exit(0);
|
|
17
15
|
}
|
|
@@ -15,6 +15,7 @@ const create_env_1 = __importDefault(require("../../utils/create-env"));
|
|
|
15
15
|
const drivers_1 = require("../../utils/drivers");
|
|
16
16
|
const questions_1 = require("./questions");
|
|
17
17
|
const generate_hash_1 = require("../../../utils/generate-hash");
|
|
18
|
+
const defaults_1 = require("../../utils/defaults");
|
|
18
19
|
async function init() {
|
|
19
20
|
const rootPath = process.cwd();
|
|
20
21
|
const { client } = await inquirer_1.default.prompt([
|
|
@@ -79,19 +80,14 @@ async function init() {
|
|
|
79
80
|
const roleID = (0, uuid_1.v4)();
|
|
80
81
|
await db('directus_roles').insert({
|
|
81
82
|
id: roleID,
|
|
82
|
-
|
|
83
|
-
icon: 'verified',
|
|
84
|
-
admin_access: true,
|
|
85
|
-
description: 'Initial administrative role with unrestricted App/API access',
|
|
83
|
+
...defaults_1.defaultAdminRole,
|
|
86
84
|
});
|
|
87
85
|
await db('directus_users').insert({
|
|
88
86
|
id: userID,
|
|
89
|
-
status: 'active',
|
|
90
87
|
email: firstUser.email,
|
|
91
88
|
password: firstUser.password,
|
|
92
|
-
first_name: 'Admin',
|
|
93
|
-
last_name: 'User',
|
|
94
89
|
role: roleID,
|
|
90
|
+
...defaults_1.defaultAdminUser,
|
|
95
91
|
});
|
|
96
92
|
await db.destroy();
|
|
97
93
|
process.stdout.write(`\nYour project has been created at ${chalk_1.default.green(rootPath)}.\n`);
|
|
@@ -35,7 +35,7 @@ const get_snapshot_diff_1 = require("../../../utils/get-snapshot-diff");
|
|
|
35
35
|
const apply_snapshot_1 = require("../../../utils/apply-snapshot");
|
|
36
36
|
const cache_1 = require("../../../cache");
|
|
37
37
|
async function apply(snapshotPath, options) {
|
|
38
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
38
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
|
|
39
39
|
const filename = path_1.default.resolve(process.cwd(), snapshotPath);
|
|
40
40
|
const database = (0, database_1.default)();
|
|
41
41
|
await (0, database_1.validateDatabaseConnection)(database);
|
|
@@ -83,12 +83,15 @@ async function apply(snapshotPath, options) {
|
|
|
83
83
|
else if (((_c = diff[0]) === null || _c === void 0 ? void 0 : _c.kind) === 'N') {
|
|
84
84
|
message += `\n - ${chalk_1.default.green('Create')} ${collection}`;
|
|
85
85
|
}
|
|
86
|
+
else if (((_d = diff[0]) === null || _d === void 0 ? void 0 : _d.kind) === 'A') {
|
|
87
|
+
message += `\n - ${chalk_1.default.blue('Update')} ${collection}`;
|
|
88
|
+
}
|
|
86
89
|
}
|
|
87
90
|
}
|
|
88
91
|
if (snapshotDiff.fields.length > 0) {
|
|
89
92
|
message += '\n\n' + chalk_1.default.black.underline.bold('Fields:');
|
|
90
93
|
for (const { collection, field, diff } of snapshotDiff.fields) {
|
|
91
|
-
if (((
|
|
94
|
+
if (((_e = diff[0]) === null || _e === void 0 ? void 0 : _e.kind) === 'E') {
|
|
92
95
|
message += `\n - ${chalk_1.default.blue('Update')} ${collection}.${field}`;
|
|
93
96
|
for (const change of diff) {
|
|
94
97
|
if (change.kind === 'E') {
|
|
@@ -97,19 +100,22 @@ async function apply(snapshotPath, options) {
|
|
|
97
100
|
}
|
|
98
101
|
}
|
|
99
102
|
}
|
|
100
|
-
else if (((
|
|
103
|
+
else if (((_f = diff[0]) === null || _f === void 0 ? void 0 : _f.kind) === 'D') {
|
|
101
104
|
message += `\n - ${chalk_1.default.red('Delete')} ${collection}.${field}`;
|
|
102
105
|
}
|
|
103
|
-
else if (((
|
|
106
|
+
else if (((_g = diff[0]) === null || _g === void 0 ? void 0 : _g.kind) === 'N') {
|
|
104
107
|
message += `\n - ${chalk_1.default.green('Create')} ${collection}.${field}`;
|
|
105
108
|
}
|
|
109
|
+
else if (((_h = diff[0]) === null || _h === void 0 ? void 0 : _h.kind) === 'A') {
|
|
110
|
+
message += `\n - ${chalk_1.default.blue('Update')} ${collection}.${field}`;
|
|
111
|
+
}
|
|
106
112
|
}
|
|
107
113
|
}
|
|
108
114
|
if (snapshotDiff.relations.length > 0) {
|
|
109
115
|
message += '\n\n' + chalk_1.default.black.underline.bold('Relations:');
|
|
110
116
|
for (const { collection, field, related_collection, diff } of snapshotDiff.relations) {
|
|
111
|
-
if (((
|
|
112
|
-
message += `\n - ${chalk_1.default.blue('Update')} ${collection}.${field}
|
|
117
|
+
if (((_j = diff[0]) === null || _j === void 0 ? void 0 : _j.kind) === 'E') {
|
|
118
|
+
message += `\n - ${chalk_1.default.blue('Update')} ${collection}.${field}`;
|
|
113
119
|
for (const change of diff) {
|
|
114
120
|
if (change.kind === 'E') {
|
|
115
121
|
const path = change.path.slice(1).join('.');
|
|
@@ -117,11 +123,21 @@ async function apply(snapshotPath, options) {
|
|
|
117
123
|
}
|
|
118
124
|
}
|
|
119
125
|
}
|
|
120
|
-
else if (((
|
|
121
|
-
message += `\n - ${chalk_1.default.red('Delete')} ${collection}.${field}
|
|
126
|
+
else if (((_k = diff[0]) === null || _k === void 0 ? void 0 : _k.kind) === 'D') {
|
|
127
|
+
message += `\n - ${chalk_1.default.red('Delete')} ${collection}.${field}`;
|
|
128
|
+
}
|
|
129
|
+
else if (((_l = diff[0]) === null || _l === void 0 ? void 0 : _l.kind) === 'N') {
|
|
130
|
+
message += `\n - ${chalk_1.default.green('Create')} ${collection}.${field}`;
|
|
131
|
+
}
|
|
132
|
+
else if (((_m = diff[0]) === null || _m === void 0 ? void 0 : _m.kind) === 'A') {
|
|
133
|
+
message += `\n - ${chalk_1.default.blue('Update')} ${collection}.${field}`;
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
continue;
|
|
122
137
|
}
|
|
123
|
-
|
|
124
|
-
|
|
138
|
+
// Related collection doesn't exist for m2a relationship types
|
|
139
|
+
if (related_collection) {
|
|
140
|
+
message += `-> ${related_collection}`;
|
|
125
141
|
}
|
|
126
142
|
}
|
|
127
143
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.defaultAdminUser = exports.defaultAdminRole = void 0;
|
|
4
|
+
exports.defaultAdminRole = {
|
|
5
|
+
name: 'Administrator',
|
|
6
|
+
icon: 'verified',
|
|
7
|
+
admin_access: true,
|
|
8
|
+
description: '$t:admin_description',
|
|
9
|
+
};
|
|
10
|
+
exports.defaultAdminUser = {
|
|
11
|
+
status: 'active',
|
|
12
|
+
first_name: 'Admin',
|
|
13
|
+
last_name: 'User',
|
|
14
|
+
};
|
|
@@ -6,44 +6,17 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
const express_1 = require("express");
|
|
7
7
|
const lodash_1 = require("lodash");
|
|
8
8
|
const ms_1 = __importDefault(require("ms"));
|
|
9
|
-
const uuid_validate_1 = __importDefault(require("uuid-validate"));
|
|
10
9
|
const constants_1 = require("../constants");
|
|
11
10
|
const database_1 = __importDefault(require("../database"));
|
|
12
11
|
const env_1 = __importDefault(require("../env"));
|
|
13
12
|
const exceptions_1 = require("../exceptions");
|
|
14
13
|
const use_collection_1 = __importDefault(require("../middleware/use-collection"));
|
|
15
14
|
const services_1 = require("../services");
|
|
16
|
-
const storage_1 = __importDefault(require("../storage"));
|
|
17
15
|
const assets_1 = require("../types/assets");
|
|
18
16
|
const async_handler_1 = __importDefault(require("../utils/async-handler"));
|
|
19
17
|
const router = (0, express_1.Router)();
|
|
20
18
|
router.use((0, use_collection_1.default)('directus_files'));
|
|
21
19
|
router.get('/:pk',
|
|
22
|
-
// Check if file exists and if you have permission to read it
|
|
23
|
-
(0, async_handler_1.default)(async (req, res, next) => {
|
|
24
|
-
var _a;
|
|
25
|
-
/**
|
|
26
|
-
* We ignore everything in the id after the first 36 characters (uuid length). This allows the
|
|
27
|
-
* user to add an optional extension, or other identifier for use in external software (#4067)
|
|
28
|
-
*/
|
|
29
|
-
const id = (_a = req.params.pk) === null || _a === void 0 ? void 0 : _a.substring(0, 36);
|
|
30
|
-
/**
|
|
31
|
-
* This is a little annoying. Postgres will error out if you're trying to search in `where`
|
|
32
|
-
* with a wrong type. In case of directus_files where id is a uuid, we'll have to verify the
|
|
33
|
-
* validity of the uuid ahead of time.
|
|
34
|
-
*/
|
|
35
|
-
const isValidUUID = (0, uuid_validate_1.default)(id, 4);
|
|
36
|
-
if (isValidUUID === false)
|
|
37
|
-
throw new exceptions_1.ForbiddenException();
|
|
38
|
-
const database = (0, database_1.default)();
|
|
39
|
-
const file = await database.select('id', 'storage', 'filename_disk').from('directus_files').where({ id }).first();
|
|
40
|
-
if (!file)
|
|
41
|
-
throw new exceptions_1.ForbiddenException();
|
|
42
|
-
const { exists } = await storage_1.default.disk(file.storage).exists(file.filename_disk);
|
|
43
|
-
if (!exists)
|
|
44
|
-
throw new exceptions_1.ForbiddenException();
|
|
45
|
-
return next();
|
|
46
|
-
}),
|
|
47
20
|
// Validate query params
|
|
48
21
|
(0, async_handler_1.default)(async (req, res, next) => {
|
|
49
22
|
const payloadService = new services_1.PayloadService('directus_settings', { schema: req.schema });
|
package/dist/controllers/auth.js
CHANGED
|
@@ -38,7 +38,9 @@ for (const authProvider of authProviders) {
|
|
|
38
38
|
}
|
|
39
39
|
router.use(`/login/${authProvider.name}`, authRouter);
|
|
40
40
|
}
|
|
41
|
-
|
|
41
|
+
if (!env_1.default.AUTH_DISABLE_DEFAULT) {
|
|
42
|
+
router.use('/login', (0, drivers_1.createLocalAuthRouter)(constants_1.DEFAULT_AUTH_PROVIDER));
|
|
43
|
+
}
|
|
42
44
|
router.post('/refresh', (0, async_handler_1.default)(async (req, res, next) => {
|
|
43
45
|
var _a;
|
|
44
46
|
const accountability = {
|
|
@@ -141,7 +143,10 @@ router.post('/password/reset', (0, async_handler_1.default)(async (req, res, nex
|
|
|
141
143
|
return next();
|
|
142
144
|
}), respond_1.respond);
|
|
143
145
|
router.get('/', (0, async_handler_1.default)(async (req, res, next) => {
|
|
144
|
-
res.locals.payload = {
|
|
146
|
+
res.locals.payload = {
|
|
147
|
+
data: (0, get_auth_providers_1.getAuthProviders)(),
|
|
148
|
+
disableDefault: env_1.default.AUTH_DISABLE_DEFAULT,
|
|
149
|
+
};
|
|
145
150
|
return next();
|
|
146
151
|
}), respond_1.respond);
|
|
147
152
|
exports.default = router;
|
|
@@ -33,7 +33,7 @@ router.get('/:type/index.js', (0, async_handler_1.default)(async (req, res) => {
|
|
|
33
33
|
throw new exceptions_1.RouteNotFoundException(req.path);
|
|
34
34
|
}
|
|
35
35
|
res.setHeader('Content-Type', 'application/javascript; charset=UTF-8');
|
|
36
|
-
res.setHeader('Cache-Control', 'no-
|
|
36
|
+
res.setHeader('Cache-Control', 'no-store');
|
|
37
37
|
res.setHeader('Vary', 'Origin, Cache-Control');
|
|
38
38
|
res.end(extensionSource);
|
|
39
39
|
}));
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { DateHelper } from '../types';
|
|
1
2
|
import { Knex } from 'knex';
|
|
2
|
-
export
|
|
3
|
+
export declare class DateHelperMSSQL extends DateHelper {
|
|
3
4
|
year(table: string, column: string): Knex.Raw;
|
|
4
5
|
month(table: string, column: string): Knex.Raw;
|
|
5
6
|
week(table: string, column: string): Knex.Raw;
|