mirror of
https://github.com/strapi/strapi.git
synced 2025-08-12 10:48:12 +00:00
Merge pull request #3822 from strapi/chore/routing-x-forwarded
Remove x-forwarded-host headers in admin panel and set default security options for development
This commit is contained in:
commit
6ed183d59d
10
README.md
10
README.md
@ -97,11 +97,11 @@ This project is currently in **Beta**. Significant breaking changes are unlikely
|
||||
|
||||
## Features
|
||||
|
||||
- **Modern Admin Panel:** Elegant, entirely customizable, and a fully extensible admin panel.
|
||||
- **Secure by default:** Reusable policies, CSRF, CORS, P3P, Xframe, XSS, and more.
|
||||
- **Plugins Oriented:** Install auth systems, content management, custom plugins, and more, in seconds.
|
||||
- **Blazing Fast:** Built on top of Node.js, Strapi delivers impressive performance.
|
||||
- **Front-end Agnostic:** Use any front-end framework, for example, React, Vue, Angular, mobile apps, or even IoT.
|
||||
- **Modern Admin Panel:** Elegant, entirely customizable and a fully extensible admin panel.
|
||||
- **Secure by default:** Reusable policies, CORS, CSP, P3P, Xframe, XSS, and more.
|
||||
- **Plugins Oriented:** Install auth system, content management, custom plugins, and more, in seconds.
|
||||
- **Blazing Fast:** Built on top of Node.js, Strapi delivers amazing performance.
|
||||
- **Front-end Agnostic:** Use any front-end framework (React, Vue, Angular, etc.), mobile apps or even IoT.
|
||||
- **Powerful CLI:** Scaffold projects and APIs on the fly.
|
||||
- **SQL & NoSQL databases:** Works with MongoDB, PostgreSQL, MySQL, MariaDB, and SQLite.
|
||||
|
||||
|
@ -44,7 +44,6 @@ The core of Strapi embraces a small list of middlewares for performances, securi
|
||||
- cors
|
||||
- cron
|
||||
- csp
|
||||
- csrf
|
||||
- favicon
|
||||
- gzip
|
||||
- hsts
|
||||
|
@ -396,10 +396,6 @@ The session doesn't work with `mongo` as a client. The package that we should us
|
||||
|
||||
**Path —** `./config/environments/**/security.json`.
|
||||
|
||||
- [`csrf`](https://en.wikipedia.org/wiki/Cross-site_request_forgery)
|
||||
- `enabled` (boolean): Enable or disable CSRF. Default value: depends on the environment.
|
||||
- `key` (string): The name of the CSRF token added to the model. Default value: `_csrf`.
|
||||
- `secret` (string): The key to place on the session object which maps to the server side token. Default value: `_csrfSecret`.
|
||||
- [`csp`](https://en.wikipedia.org/wiki/Content_Security_Policy)
|
||||
- `enabled` (boolean): Enable or disable CSP to avoid Cross Site Scripting (XSS) and data injection attacks.
|
||||
- [`p3p`](https://en.wikipedia.org/wiki/P3P)
|
||||
|
@ -15,6 +15,17 @@ You can simply copy and paste this code in your own controller file to customize
|
||||
In the following example we will consider your controller, service and model is named `product`
|
||||
:::
|
||||
|
||||
#### Utils
|
||||
|
||||
First require the utility functions
|
||||
|
||||
```js
|
||||
const { parseMultipartData, sanitizeEntity } = require('strapi-utils');
|
||||
```
|
||||
|
||||
- `parseMultipartData`: This function parses strapi's formData format.
|
||||
- `sanitizeEntity`: This function removes all private fields from the model and its relations.
|
||||
|
||||
#### `find`
|
||||
|
||||
```js
|
||||
@ -25,11 +36,15 @@ module.exports = {
|
||||
* @return {Array}
|
||||
*/
|
||||
|
||||
find(ctx) {
|
||||
async find(ctx) {
|
||||
let entities;
|
||||
if (ctx.query._q) {
|
||||
return strapi.services.product.search(ctx.query);
|
||||
entities = await service.search(ctx.query);
|
||||
} else {
|
||||
entities = await service.find(ctx.query);
|
||||
}
|
||||
return strapi.services.product.find(ctx.query);
|
||||
|
||||
return entities.map(entity => sanitizeEntity(entity, { model }));
|
||||
},
|
||||
};
|
||||
```
|
||||
@ -44,8 +59,9 @@ module.exports = {
|
||||
* @return {Object}
|
||||
*/
|
||||
|
||||
findOne(ctx) {
|
||||
return strapi.services.product.findOne(ctx.params);
|
||||
async findOne(ctx) {
|
||||
const entity = await service.findOne(ctx.params);
|
||||
return sanitizeEntity(entity, { model });
|
||||
},
|
||||
};
|
||||
```
|
||||
@ -62,9 +78,9 @@ module.exports = {
|
||||
|
||||
count(ctx) {
|
||||
if (ctx.query._q) {
|
||||
return strapi.services.product.countSearch(ctx.query);
|
||||
return service.countSearch(ctx.query);
|
||||
}
|
||||
return strapi.services.product.count(ctx.query);
|
||||
return service.count(ctx.query);
|
||||
},
|
||||
};
|
||||
```
|
||||
@ -79,14 +95,15 @@ module.exports = {
|
||||
* @return {Object}
|
||||
*/
|
||||
|
||||
create(ctx) {
|
||||
async create(ctx) {
|
||||
let entity;
|
||||
if (ctx.is('multipart')) {
|
||||
// Parses strapi's formData format
|
||||
const { data, files } = this.parseMultipartData(ctx);
|
||||
return service.create(data, { files });
|
||||
const { data, files } = parseMultipartData(ctx);
|
||||
entity = await service.create(data, { files });
|
||||
} else {
|
||||
entity = await service.create(ctx.request.body);
|
||||
}
|
||||
|
||||
return service.create(ctx.request.body);
|
||||
return sanitizeEntity(entity, { model });
|
||||
},
|
||||
};
|
||||
```
|
||||
@ -101,14 +118,16 @@ module.exports = {
|
||||
* @return {Object}
|
||||
*/
|
||||
|
||||
update(ctx) {
|
||||
async update(ctx) {
|
||||
let entity;
|
||||
if (ctx.is('multipart')) {
|
||||
// Parses strapi's formData format
|
||||
const { data, files } = this.parseMultipartData(ctx);
|
||||
return service.update(ctx.params, data, { files });
|
||||
const { data, files } = parseMultipartData(ctx);
|
||||
entity = await service.update(ctx.params, data, { files });
|
||||
} else {
|
||||
entity = await service.update(ctx.params, ctx.request.body);
|
||||
}
|
||||
|
||||
return service.update(ctx.params, ctx.request.body);
|
||||
return sanitizeEntity(entity, { model });
|
||||
},
|
||||
};
|
||||
```
|
||||
@ -123,8 +142,9 @@ module.exports = {
|
||||
* @return {Object}
|
||||
*/
|
||||
|
||||
delete(ctx) {
|
||||
return strapi.services.product.delete(ctx.params);
|
||||
async delete(ctx) {
|
||||
const entity = await service.delete(ctx.params);
|
||||
return sanitizeEntity(entity, { model });
|
||||
},
|
||||
};
|
||||
```
|
||||
|
@ -1,30 +1,28 @@
|
||||
{
|
||||
"csrf": {
|
||||
"enabled": false,
|
||||
"key": "_csrf",
|
||||
"secret": "_csrfSecret"
|
||||
},
|
||||
"csp": {
|
||||
"enabled": false,
|
||||
"policy": {
|
||||
"default-src": "'self'"
|
||||
}
|
||||
"enabled": true,
|
||||
"policy": [
|
||||
{
|
||||
"img-src": "'self' http:"
|
||||
},
|
||||
"block-all-mixed-content"
|
||||
]
|
||||
},
|
||||
"p3p": {
|
||||
"enabled": false,
|
||||
"value": ""
|
||||
},
|
||||
"hsts": {
|
||||
"enabled": false,
|
||||
"enabled": true,
|
||||
"maxAge": 31536000,
|
||||
"includeSubDomains": true
|
||||
},
|
||||
"xframe": {
|
||||
"enabled": false,
|
||||
"enabled": true,
|
||||
"value": "SAMEORIGIN"
|
||||
},
|
||||
"xss": {
|
||||
"enabled": false,
|
||||
"enabled": true,
|
||||
"mode": "block"
|
||||
},
|
||||
"cors": {
|
||||
|
@ -1,9 +1,4 @@
|
||||
{
|
||||
"csrf": {
|
||||
"enabled": false,
|
||||
"key": "_csrf",
|
||||
"secret": "_csrfSecret"
|
||||
},
|
||||
"csp": {
|
||||
"enabled": true,
|
||||
"policy": [
|
||||
|
@ -1,9 +1,4 @@
|
||||
{
|
||||
"csrf": {
|
||||
"enabled": false,
|
||||
"key": "_csrf",
|
||||
"secret": "_csrfSecret"
|
||||
},
|
||||
"csp": {
|
||||
"enabled": true,
|
||||
"policy": [
|
||||
|
@ -3,6 +3,10 @@
|
||||
const execa = require('execa');
|
||||
const _ = require('lodash');
|
||||
|
||||
const formatError = error => [
|
||||
{ messages: [{ id: error.id, message: error.message, field: error.field }] },
|
||||
];
|
||||
|
||||
/**
|
||||
* A set of functions called "actions" for `Admin`
|
||||
*/
|
||||
@ -121,9 +125,38 @@ module.exports = {
|
||||
async create(ctx) {
|
||||
const { email, username, password, blocked } = ctx.request.body;
|
||||
|
||||
if (!email) return ctx.badRequest('missing.email');
|
||||
if (!username) return ctx.badRequest('missing.username');
|
||||
if (!password) return ctx.badRequest('missing.password');
|
||||
if (!email) {
|
||||
return ctx.badRequest(
|
||||
null,
|
||||
formatError({
|
||||
id: 'missing.email',
|
||||
message: 'Missing email',
|
||||
field: ['email'],
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (!username) {
|
||||
return ctx.badRequest(
|
||||
null,
|
||||
formatError({
|
||||
id: 'missing.username',
|
||||
message: 'Missing username',
|
||||
field: ['username'],
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (!password) {
|
||||
return ctx.badRequest(
|
||||
null,
|
||||
formatError({
|
||||
id: 'missing.password',
|
||||
message: 'Missing password',
|
||||
field: ['password'],
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
const adminsWithSameEmail = await strapi
|
||||
.query('administrator', 'admin')
|
||||
@ -136,33 +169,22 @@ module.exports = {
|
||||
if (adminsWithSameEmail) {
|
||||
return ctx.badRequest(
|
||||
null,
|
||||
ctx.request.admin
|
||||
? [
|
||||
{
|
||||
messages: [
|
||||
{ id: 'Auth.form.error.email.taken', field: ['email'] },
|
||||
],
|
||||
},
|
||||
]
|
||||
: 'email.alreadyTaken'
|
||||
formatError({
|
||||
id: 'Auth.form.error.email.taken',
|
||||
message: 'Email already taken',
|
||||
field: ['email'],
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (adminsWithSameUsername) {
|
||||
return ctx.badRequest(
|
||||
null,
|
||||
ctx.request.admin
|
||||
? [
|
||||
{
|
||||
messages: [
|
||||
{
|
||||
id: 'Auth.form.error.username.taken',
|
||||
field: ['username'],
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
: 'username.alreadyTaken.'
|
||||
formatError({
|
||||
id: 'Auth.form.error.username.taken',
|
||||
message: 'Username already taken',
|
||||
field: ['username'],
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@ -189,10 +211,38 @@ module.exports = {
|
||||
const { id } = ctx.params;
|
||||
const { email, username, password, blocked } = ctx.request.body;
|
||||
|
||||
if (!email) return ctx.badRequest('Missing email');
|
||||
if (!username) return ctx.badRequest('Missing username');
|
||||
if (!password) return ctx.badRequest('Missing password');
|
||||
if (!email) {
|
||||
return ctx.badRequest(
|
||||
null,
|
||||
formatError({
|
||||
id: 'missing.email',
|
||||
message: 'Missing email',
|
||||
field: ['email'],
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (!username) {
|
||||
return ctx.badRequest(
|
||||
null,
|
||||
formatError({
|
||||
id: 'missing.username',
|
||||
message: 'Missing username',
|
||||
field: ['username'],
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (!password) {
|
||||
return ctx.badRequest(
|
||||
null,
|
||||
formatError({
|
||||
id: 'missing.password',
|
||||
message: 'Missing password',
|
||||
field: ['password'],
|
||||
})
|
||||
);
|
||||
}
|
||||
const admin = await strapi
|
||||
.query('administrator', 'admin')
|
||||
.findOne(ctx.params);
|
||||
@ -209,15 +259,11 @@ module.exports = {
|
||||
if (adminsWithSameEmail && adminsWithSameEmail.id !== admin.id) {
|
||||
return ctx.badRequest(
|
||||
null,
|
||||
ctx.request.admin
|
||||
? [
|
||||
{
|
||||
messages: [
|
||||
{ id: 'Auth.form.error.email.taken', field: ['email'] },
|
||||
],
|
||||
},
|
||||
]
|
||||
: 'Email is already taken.'
|
||||
formatError({
|
||||
id: 'Auth.form.error.email.taken',
|
||||
message: 'Email already taken',
|
||||
field: ['email'],
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -231,18 +277,11 @@ module.exports = {
|
||||
if (adminsWithSameUsername && adminsWithSameUsername.id !== admin.id) {
|
||||
return ctx.badRequest(
|
||||
null,
|
||||
ctx.request.admin
|
||||
? [
|
||||
{
|
||||
messages: [
|
||||
{
|
||||
id: 'Auth.form.error.username.taken',
|
||||
field: ['username'],
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
: 'Username is already taken.'
|
||||
formatError({
|
||||
id: 'Auth.form.error.username.taken',
|
||||
message: 'Username already taken',
|
||||
field: ['username'],
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,9 @@ const crypto = require('crypto');
|
||||
const _ = require('lodash');
|
||||
|
||||
const emailRegExp = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
||||
const formatError = error => [
|
||||
{ messages: [{ id: error.id, message: error.message, field: error.field }] },
|
||||
];
|
||||
|
||||
module.exports = {
|
||||
async callback(ctx) {
|
||||
@ -20,9 +23,10 @@ module.exports = {
|
||||
if (!params.identifier) {
|
||||
return ctx.badRequest(
|
||||
null,
|
||||
ctx.request.admin
|
||||
? [{ messages: [{ id: 'Auth.form.error.email.provide' }] }]
|
||||
: 'Please provide your username or your e-mail.'
|
||||
formatError({
|
||||
id: 'Auth.form.error.email.provide',
|
||||
message: 'Please provide your username or your e-mail.',
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@ -30,9 +34,10 @@ module.exports = {
|
||||
if (!params.password) {
|
||||
return ctx.badRequest(
|
||||
null,
|
||||
ctx.request.admin
|
||||
? [{ messages: [{ id: 'Auth.form.error.password.provide' }] }]
|
||||
: 'Please provide your password.'
|
||||
formatError({
|
||||
id: 'Auth.form.error.password.provide',
|
||||
message: 'Please provide your password.',
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@ -54,18 +59,20 @@ module.exports = {
|
||||
if (!admin) {
|
||||
return ctx.badRequest(
|
||||
null,
|
||||
ctx.request.admin
|
||||
? [{ messages: [{ id: 'Auth.form.error.invalid' }] }]
|
||||
: 'Identifier or password invalid.'
|
||||
formatError({
|
||||
id: 'Auth.form.error.invalid',
|
||||
message: 'Identifier or password invalid.',
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (admin.blocked === true) {
|
||||
return ctx.badRequest(
|
||||
null,
|
||||
ctx.request.admin
|
||||
? [{ messages: [{ id: 'Auth.form.error.blocked' }] }]
|
||||
: 'Your account has been blocked by the administrator.'
|
||||
formatError({
|
||||
id: 'Auth.form.error.blocked',
|
||||
message: 'Your account has been blocked by the administrator.',
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@ -77,9 +84,10 @@ module.exports = {
|
||||
if (!validPassword) {
|
||||
return ctx.badRequest(
|
||||
null,
|
||||
ctx.request.admin
|
||||
? [{ messages: [{ id: 'Auth.form.error.invalid' }] }]
|
||||
: 'Identifier or password invalid.'
|
||||
formatError({
|
||||
id: 'Auth.form.error.invalid',
|
||||
message: 'Identifier or password invalid.',
|
||||
})
|
||||
);
|
||||
} else {
|
||||
admin.isAdmin = true;
|
||||
@ -98,9 +106,10 @@ module.exports = {
|
||||
if (!params.username) {
|
||||
return ctx.badRequest(
|
||||
null,
|
||||
ctx.request.admin
|
||||
? [{ messages: [{ id: 'Auth.form.error.username.provide' }] }]
|
||||
: 'Please provide your username.'
|
||||
formatError({
|
||||
id: 'Auth.form.error.username.provide',
|
||||
message: 'Please provide your username.',
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@ -108,9 +117,10 @@ module.exports = {
|
||||
if (!params.email) {
|
||||
return ctx.badRequest(
|
||||
null,
|
||||
ctx.request.admin
|
||||
? [{ messages: [{ id: 'Auth.form.error.email.provide' }] }]
|
||||
: 'Please provide your email.'
|
||||
formatError({
|
||||
id: 'Auth.form.error.email.provide',
|
||||
message: 'Please provide your email.',
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@ -127,9 +137,10 @@ module.exports = {
|
||||
if (!params.password) {
|
||||
return ctx.badRequest(
|
||||
null,
|
||||
ctx.request.admin
|
||||
? [{ messages: [{ id: 'Auth.form.error.password.provide' }] }]
|
||||
: 'Please provide your password.'
|
||||
formatError({
|
||||
id: 'Auth.form.error.password.provide',
|
||||
message: 'Please provide your password.',
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@ -141,9 +152,10 @@ module.exports = {
|
||||
if (admins.length > 0) {
|
||||
return ctx.badRequest(
|
||||
null,
|
||||
ctx.request.admin
|
||||
? [{ messages: [{ id: 'Auth.form.error.admin.exist' }] }]
|
||||
: "You can't register a new admin."
|
||||
formatError({
|
||||
id: 'Auth.form.error.admin.exist',
|
||||
message: "You can't register a new admin",
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@ -158,9 +170,10 @@ module.exports = {
|
||||
if (admin) {
|
||||
return ctx.badRequest(
|
||||
null,
|
||||
ctx.request.admin
|
||||
? [{ messages: [{ id: 'Auth.form.error.email.taken' }] }]
|
||||
: 'Email is already taken.'
|
||||
formatError({
|
||||
id: 'Auth.form.error.email.taken',
|
||||
message: 'Email is already taken',
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@ -180,13 +193,13 @@ module.exports = {
|
||||
} catch (err) {
|
||||
strapi.log.error(err);
|
||||
const adminError = _.includes(err.message, 'username')
|
||||
? 'Auth.form.error.username.taken'
|
||||
: 'Auth.form.error.email.taken';
|
||||
? {
|
||||
id: 'Auth.form.error.username.taken',
|
||||
message: 'Username already taken',
|
||||
}
|
||||
: { id: 'Auth.form.error.email.taken', message: 'Email already taken' };
|
||||
|
||||
ctx.badRequest(
|
||||
null,
|
||||
ctx.request.admin ? [{ messages: [{ id: adminError }] }] : err.message
|
||||
);
|
||||
ctx.badRequest(null, formatError(adminError));
|
||||
}
|
||||
},
|
||||
|
||||
@ -196,17 +209,42 @@ module.exports = {
|
||||
...ctx.params,
|
||||
};
|
||||
|
||||
if (!password) return ctx.badRequest('Missing password');
|
||||
if (!passwordConfirmation)
|
||||
return ctx.badRequest('Missing passwordConfirmation');
|
||||
if (!code) return ctx.badRequest('Missing code');
|
||||
if (!password) {
|
||||
return ctx.badRequest(
|
||||
null,
|
||||
formatError({
|
||||
id: 'missing.password',
|
||||
message: 'Missing password',
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (!passwordConfirmation) {
|
||||
return ctx.badRequest(
|
||||
formatError({
|
||||
id: 'missing.passwordConfirmation',
|
||||
message: 'Missing passwordConfirmation',
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (!code) {
|
||||
return ctx.badRequest(
|
||||
null,
|
||||
formatError({
|
||||
id: 'missing.code',
|
||||
message: 'Missing code',
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (password !== passwordConfirmation) {
|
||||
return ctx.badRequest(
|
||||
null,
|
||||
ctx.request.admin
|
||||
? [{ messages: [{ id: 'Auth.form.error.password.matching' }] }]
|
||||
: 'Passwords do not match.'
|
||||
formatError({
|
||||
id: 'Auth.form.error.password.matching',
|
||||
message: 'Passwords do not match.',
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@ -217,9 +255,10 @@ module.exports = {
|
||||
if (!admin) {
|
||||
return ctx.badRequest(
|
||||
null,
|
||||
ctx.request.admin
|
||||
? [{ messages: [{ id: 'Auth.form.error.code.provide' }] }]
|
||||
: 'Incorrect code provided.'
|
||||
formatError({
|
||||
id: 'Auth.form.error.code.provide',
|
||||
message: 'Incorrect code provided.',
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@ -241,8 +280,24 @@ module.exports = {
|
||||
async forgotPassword(ctx) {
|
||||
const { email, url } = ctx.request.body;
|
||||
|
||||
if (!email) return ctx.badRequest('Missing email');
|
||||
if (!url) return ctx.badRequest('Missing url');
|
||||
if (!email) {
|
||||
return ctx.badRequest(
|
||||
null,
|
||||
formatError({
|
||||
id: 'missing.email',
|
||||
message: 'Missing email',
|
||||
})
|
||||
);
|
||||
}
|
||||
if (!url) {
|
||||
return ctx.badRequest(
|
||||
null,
|
||||
formatError({
|
||||
id: 'missing.url',
|
||||
message: 'Missing url',
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// Find the admin thanks to his email.
|
||||
const admin = await strapi
|
||||
@ -253,9 +308,10 @@ module.exports = {
|
||||
if (!admin) {
|
||||
return ctx.badRequest(
|
||||
null,
|
||||
ctx.request.admin
|
||||
? [{ messages: [{ id: 'Auth.form.error.user.not-exist' }] }]
|
||||
: 'This email does not exist.'
|
||||
formatError({
|
||||
id: 'Auth.form.error.user.not-exist',
|
||||
message: 'This email does not exit',
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1,30 +1,28 @@
|
||||
{
|
||||
"csrf": {
|
||||
"enabled": false,
|
||||
"key": "_csrf",
|
||||
"secret": "_csrfSecret"
|
||||
},
|
||||
"csp": {
|
||||
"enabled": false,
|
||||
"policy": {
|
||||
"default-src": "'self'"
|
||||
}
|
||||
"enabled": true,
|
||||
"policy": [
|
||||
{
|
||||
"img-src": "'self' http:"
|
||||
},
|
||||
"block-all-mixed-content"
|
||||
]
|
||||
},
|
||||
"p3p": {
|
||||
"enabled": false,
|
||||
"value": ""
|
||||
},
|
||||
"hsts": {
|
||||
"enabled": false,
|
||||
"enabled": true,
|
||||
"maxAge": 31536000,
|
||||
"includeSubDomains": true
|
||||
},
|
||||
"xframe": {
|
||||
"enabled": false,
|
||||
"enabled": true,
|
||||
"value": "SAMEORIGIN"
|
||||
},
|
||||
"xss": {
|
||||
"enabled": false,
|
||||
"enabled": true,
|
||||
"mode": "block"
|
||||
},
|
||||
"cors": {
|
||||
|
@ -1,9 +1,4 @@
|
||||
{
|
||||
"csrf": {
|
||||
"enabled": false,
|
||||
"key": "_csrf",
|
||||
"secret": "_csrfSecret"
|
||||
},
|
||||
"csp": {
|
||||
"enabled": true,
|
||||
"policy": [
|
||||
|
@ -1,9 +1,4 @@
|
||||
{
|
||||
"csrf": {
|
||||
"enabled": false,
|
||||
"key": "_csrf",
|
||||
"secret": "_csrfSecret"
|
||||
},
|
||||
"csp": {
|
||||
"enabled": true,
|
||||
"policy": [
|
||||
|
@ -135,10 +135,7 @@ export default function request(...args) {
|
||||
{
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
options.headers,
|
||||
{
|
||||
'X-Forwarded-Host': 'strapi',
|
||||
}
|
||||
options.headers
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -178,7 +178,7 @@ module.exports = function createQueryBuilder({ model, modelKey, strapi }) {
|
||||
const runDelete = async trx => {
|
||||
await deleteGroups(entry, { transacting: trx });
|
||||
await model.forge(params).destroy({ transacting: trx, require: false });
|
||||
return entry;
|
||||
return entry.toJSON();
|
||||
};
|
||||
|
||||
return wrapTransaction(runDelete, { transacting });
|
||||
@ -217,7 +217,8 @@ module.exports = function createQueryBuilder({ model, modelKey, strapi }) {
|
||||
})
|
||||
.fetchAll({
|
||||
withRelated: populate,
|
||||
});
|
||||
})
|
||||
.then(results => results.toJSON());
|
||||
}
|
||||
|
||||
function countSearch(params) {
|
||||
|
@ -199,9 +199,9 @@ module.exports = ({ model, modelKey, strapi }) => {
|
||||
model,
|
||||
filters,
|
||||
populate: populateOpt,
|
||||
}).then(results => {
|
||||
return results.map(result => (result ? result.toObject() : null));
|
||||
});
|
||||
}).then(results =>
|
||||
results.map(result => (result ? result.toObject() : null))
|
||||
);
|
||||
}
|
||||
|
||||
async function findOne(params, populate) {
|
||||
@ -326,7 +326,7 @@ module.exports = ({ model, modelKey, strapi }) => {
|
||||
})
|
||||
);
|
||||
|
||||
return entry;
|
||||
return entry.toObject ? entry.toObject() : null;
|
||||
}
|
||||
|
||||
function search(params, populate) {
|
||||
@ -340,7 +340,10 @@ module.exports = ({ model, modelKey, strapi }) => {
|
||||
.sort(filters.sort)
|
||||
.skip(filters.start)
|
||||
.limit(filters.limit)
|
||||
.populate(populate || defaultPopulate);
|
||||
.populate(populate || defaultPopulate)
|
||||
.then(results =>
|
||||
results.map(result => (result ? result.toObject() : null))
|
||||
);
|
||||
}
|
||||
|
||||
function countSearch(params) {
|
||||
|
@ -673,9 +673,7 @@ class Wysiwyg extends React.Component {
|
||||
uploadFile = files => {
|
||||
const formData = new FormData();
|
||||
formData.append('files', files[0]);
|
||||
const headers = {
|
||||
'X-Forwarded-Host': 'strapi',
|
||||
};
|
||||
const headers = {};
|
||||
|
||||
let newEditorState = this.getEditorState();
|
||||
|
||||
|
@ -265,7 +265,7 @@ function EditView({
|
||||
});
|
||||
|
||||
// Change the request helper default headers so we can pass a FormData
|
||||
const headers = { 'X-Forwarded-Host': 'strapi' };
|
||||
const headers = {};
|
||||
const method = isCreatingEntry ? 'POST' : 'PUT';
|
||||
const endPoint = isCreatingEntry ? slug : `${slug}/${id}`;
|
||||
|
||||
|
@ -95,12 +95,13 @@ module.exports = {
|
||||
strapi.emit('didCreateFirstContentTypeEntry', ctx.params, source);
|
||||
} catch (error) {
|
||||
strapi.log.error(error);
|
||||
ctx.badRequest(
|
||||
null,
|
||||
ctx.request.admin
|
||||
? [{ messages: [{ id: error.message, field: error.field }] }]
|
||||
: error.message
|
||||
);
|
||||
ctx.badRequest(null, [
|
||||
{
|
||||
messages: [
|
||||
{ id: error.message, message: error.message, field: error.field },
|
||||
],
|
||||
},
|
||||
]);
|
||||
}
|
||||
},
|
||||
|
||||
@ -131,12 +132,13 @@ module.exports = {
|
||||
}
|
||||
} catch (error) {
|
||||
strapi.log.error(error);
|
||||
ctx.badRequest(
|
||||
null,
|
||||
ctx.request.admin
|
||||
? [{ messages: [{ id: error.message, field: error.field }] }]
|
||||
: error.message
|
||||
);
|
||||
ctx.badRequest(null, [
|
||||
{
|
||||
messages: [
|
||||
{ id: error.message, message: error.message, field: error.field },
|
||||
],
|
||||
},
|
||||
]);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -9,18 +9,26 @@
|
||||
const _ = require('lodash');
|
||||
|
||||
module.exports = {
|
||||
send: async (ctx) => {
|
||||
send: async ctx => {
|
||||
// Retrieve provider configuration.
|
||||
const config = await strapi.store({
|
||||
environment: strapi.config.environment,
|
||||
type: 'plugin',
|
||||
name: 'email'
|
||||
}).get({ key: 'provider' });
|
||||
const config = await strapi
|
||||
.store({
|
||||
environment: strapi.config.environment,
|
||||
type: 'plugin',
|
||||
name: 'email',
|
||||
})
|
||||
.get({ key: 'provider' });
|
||||
|
||||
// Verify if the file email is enable.
|
||||
if (config.enabled === false) {
|
||||
strapi.log.error('Email is disabled');
|
||||
return ctx.badRequest(null, ctx.request.admin ? [{ messages: [{ id: 'Email.status.disabled' }] }] : 'Emailis disabled');
|
||||
return ctx.badRequest(null, [
|
||||
{
|
||||
messages: [
|
||||
{ id: 'Email.status.disabled', message: 'Emails disabled' },
|
||||
],
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
// Something is wrong
|
||||
@ -36,33 +44,40 @@ module.exports = {
|
||||
ctx.send({});
|
||||
},
|
||||
|
||||
getEnvironments: async (ctx) => {
|
||||
const environments = _.map(_.keys(strapi.config.environments), environment => {
|
||||
return {
|
||||
name: environment,
|
||||
active: (strapi.config.environment === environment)
|
||||
};
|
||||
});
|
||||
getEnvironments: async ctx => {
|
||||
const environments = _.map(
|
||||
_.keys(strapi.config.environments),
|
||||
environment => {
|
||||
return {
|
||||
name: environment,
|
||||
active: strapi.config.environment === environment,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
ctx.send({ environments });
|
||||
},
|
||||
|
||||
getSettings: async (ctx) => {
|
||||
let config = await strapi.plugins.email.services.email.getProviderConfig(ctx.params.environment);
|
||||
getSettings: async ctx => {
|
||||
let config = await strapi.plugins.email.services.email.getProviderConfig(
|
||||
ctx.params.environment
|
||||
);
|
||||
|
||||
ctx.send({
|
||||
providers: strapi.plugins.email.config.providers,
|
||||
config
|
||||
config,
|
||||
});
|
||||
},
|
||||
|
||||
updateSettings: async (ctx) => {
|
||||
await strapi.store({
|
||||
environment: ctx.params.environment,
|
||||
type: 'plugin',
|
||||
name: 'email'
|
||||
}).set({key: 'provider', value: ctx.request.body});
|
||||
updateSettings: async ctx => {
|
||||
await strapi
|
||||
.store({
|
||||
environment: ctx.params.environment,
|
||||
type: 'plugin',
|
||||
name: 'email',
|
||||
})
|
||||
.set({ key: 'provider', value: ctx.request.body });
|
||||
|
||||
ctx.send({ok: true});
|
||||
ctx.send({ ok: true });
|
||||
},
|
||||
};
|
||||
|
@ -54,11 +54,6 @@
|
||||
"form.security.description": "تكوين إعدادات الأمان الخاصة بك.",
|
||||
"form.security.item.cors": "Cors",
|
||||
"form.security.item.cors.origin": "Origin",
|
||||
"form.security.item.csrf": "CSRF",
|
||||
"form.security.item.csrf.angular": "الزاوي",
|
||||
"form.security.item.csrf.cookie": "الكعكات",
|
||||
"form.security.item.csrf.key": "المفتاح",
|
||||
"form.security.item.csrf.secret": "الحماية",
|
||||
"form.security.item.hsts": "المضيفين",
|
||||
"form.security.item.hsts.includeSubDomains": "تضمين المجال الفرعي",
|
||||
"form.security.item.hsts.maxAge": "أقصى عمر",
|
||||
@ -629,4 +624,4 @@
|
||||
"strapi.notification.success.languageAdd": "تمت إضافة اللغة بنجاح.",
|
||||
"strapi.notification.success.languageDelete": "تم حذف اللغة بنجاح.",
|
||||
"strapi.notification.success.settingsEdit": "تم تحديث الإعدادات بنجاح."
|
||||
}
|
||||
}
|
||||
|
@ -54,11 +54,6 @@
|
||||
"form.security.description": "Verwalte deine Sicherheitseinstellungen.",
|
||||
"form.security.item.cors": "Cors",
|
||||
"form.security.item.cors.origin": "Origin",
|
||||
"form.security.item.csrf": "CSRF",
|
||||
"form.security.item.csrf.angular": "Angular",
|
||||
"form.security.item.csrf.cookie": "Cookie",
|
||||
"form.security.item.csrf.key": "Key",
|
||||
"form.security.item.csrf.secret": "Secret",
|
||||
"form.security.item.hsts": "HOSTS",
|
||||
"form.security.item.hsts.includeSubDomains": "Subdomain mit einschließen",
|
||||
"form.security.item.hsts.maxAge": "Max Age",
|
||||
|
@ -54,11 +54,6 @@
|
||||
"form.security.description": "Configure your security settings.",
|
||||
"form.security.item.cors": "Cors",
|
||||
"form.security.item.cors.origin": "Origin",
|
||||
"form.security.item.csrf": "CSRF",
|
||||
"form.security.item.csrf.angular": "Angular",
|
||||
"form.security.item.csrf.cookie": "Cookie",
|
||||
"form.security.item.csrf.key": "Key",
|
||||
"form.security.item.csrf.secret": "Secret",
|
||||
"form.security.item.hsts": "HOSTS",
|
||||
"form.security.item.hsts.includeSubDomains": "Include Sub Domain",
|
||||
"form.security.item.hsts.maxAge": "Max Age",
|
||||
|
@ -54,11 +54,6 @@
|
||||
"form.security.description": "Configurar las opciones de seguridad.",
|
||||
"form.security.item.cors": "Cors",
|
||||
"form.security.item.cors.origin": "Origen",
|
||||
"form.security.item.csrf": "CSRF",
|
||||
"form.security.item.csrf.angular": "Angular",
|
||||
"form.security.item.csrf.cookie": "Cookie",
|
||||
"form.security.item.csrf.key": "Clave",
|
||||
"form.security.item.csrf.secret": "Secreto",
|
||||
"form.security.item.hsts": "HOSTS",
|
||||
"form.security.item.hsts.includeSubDomains": "Incluir Sub Dominio",
|
||||
"form.security.item.hsts.maxAge": "Edad Máxima",
|
||||
|
@ -54,12 +54,6 @@
|
||||
"form.security.description": "Configurations de sécurité.",
|
||||
"form.security.item.cors": "Cors",
|
||||
"form.security.item.cors.origin": "Origine",
|
||||
"form.security.item.csrf": "CSRF",
|
||||
"form.security.item.csrf.angular": "Angular",
|
||||
"form.security.item.csrf.cookie": "Cookie",
|
||||
"form.security.item.csrf.key": "Clé",
|
||||
"form.security.item.csrf.secret": "Clé secrète",
|
||||
"form.security.item.hsts": "HOSTS",
|
||||
"form.security.item.hsts.includeSubDomains": "Inclure les sous-domaines",
|
||||
"form.security.item.hsts.maxAge": "Max Age",
|
||||
"form.security.item.hsts.preload": "Preload",
|
||||
|
@ -54,11 +54,6 @@
|
||||
"form.security.description": "Configurare le impostazioni di sicurezza.",
|
||||
"form.security.item.cors": "Cors",
|
||||
"form.security.item.cors.origin": "Origine",
|
||||
"form.security.item.csrf": "CSRF",
|
||||
"form.security.item.csrf.angular": "Angolare",
|
||||
"form.security.item.csrf.cookie": "Cookie",
|
||||
"form.security.item.csrf.key": "Chiave",
|
||||
"form.security.item.csrf.secret": "Segreto",
|
||||
"form.security.item.hsts": "OSPITA",
|
||||
"form.security.item.hsts.includeSubDomains": "Includi domini secondari",
|
||||
"form.security.item.hsts.maxAge": "Età Max",
|
||||
@ -629,4 +624,4 @@
|
||||
"strapi.notification.success.languageAdd": "Il linguaggio è stato aggiunto con successo.",
|
||||
"strapi.notification.success.languageDelete": "Il linguaggio è stato eliminato con successo.",
|
||||
"strapi.notification.success.settingsEdit": "Le impostazioni sono state aggiornate con successo."
|
||||
}
|
||||
}
|
||||
|
@ -54,11 +54,6 @@
|
||||
"form.security.description": "セキュリティ設定を構成します。",
|
||||
"form.security.item.cors": "Cors",
|
||||
"form.security.item.cors.origin": "Origin",
|
||||
"form.security.item.csrf": "CSRF",
|
||||
"form.security.item.csrf.angular": "Angular",
|
||||
"form.security.item.csrf.cookie": "Cookie",
|
||||
"form.security.item.csrf.key": "Key",
|
||||
"form.security.item.csrf.secret": "Secret",
|
||||
"form.security.item.hsts": "HOSTS",
|
||||
"form.security.item.hsts.includeSubDomains": "サブドメインを含める",
|
||||
"form.security.item.hsts.maxAge": "Max Age",
|
||||
|
@ -54,11 +54,6 @@
|
||||
"form.security.description": "보안 환경을 설정하세요.",
|
||||
"form.security.item.cors": "Cors",
|
||||
"form.security.item.cors.origin": "Origin",
|
||||
"form.security.item.csrf": "CSRF",
|
||||
"form.security.item.csrf.angular": "앵귤러 (Angular)",
|
||||
"form.security.item.csrf.cookie": "쿠키 (Cookie)",
|
||||
"form.security.item.csrf.key": "키 (Key)",
|
||||
"form.security.item.csrf.secret": "시크릿 (Secret)",
|
||||
"form.security.item.hsts": "호스트 (HOSTS)",
|
||||
"form.security.item.hsts.includeSubDomains": "서브 도메인 포함",
|
||||
"form.security.item.hsts.maxAge": "만료 (Max Age)",
|
||||
@ -629,4 +624,4 @@
|
||||
"strapi.notification.success.languageAdd": "언어를 추가했습니다.",
|
||||
"strapi.notification.success.languageDelete": "언어를 제거했습니다.",
|
||||
"strapi.notification.success.settingsEdit": "설정을 업데이트했습니다."
|
||||
}
|
||||
}
|
||||
|
@ -54,11 +54,6 @@
|
||||
"form.security.description": "Configureer je beveiliging instellingen.",
|
||||
"form.security.item.cors": "Cors",
|
||||
"form.security.item.cors.origin": "Origin",
|
||||
"form.security.item.csrf": "CSRF",
|
||||
"form.security.item.csrf.angular": "Angular",
|
||||
"form.security.item.csrf.cookie": "Cookie",
|
||||
"form.security.item.csrf.key": "Key",
|
||||
"form.security.item.csrf.secret": "Secret",
|
||||
"form.security.item.hsts": "HOSTS",
|
||||
"form.security.item.hsts.includeSubDomains": "Inclusief Sub Domain",
|
||||
"form.security.item.hsts.maxAge": "Max Leeftijd",
|
||||
@ -634,4 +629,4 @@
|
||||
"strapi.notification.success.languageAdd": "De taal is succesvol toegevoegd.",
|
||||
"strapi.notification.success.languageDelete": "De taal is succesvol verwijderd.",
|
||||
"strapi.notification.success.settingsEdit": "De instellingen zijn succesvol geüpdatet"
|
||||
}
|
||||
}
|
||||
|
@ -54,11 +54,6 @@
|
||||
"form.security.description": "Konfiguruj ustawienia bezpieczeństwa.",
|
||||
"form.security.item.cors": "Cors",
|
||||
"form.security.item.cors.origin": "Źródło",
|
||||
"form.security.item.csrf": "CSRF",
|
||||
"form.security.item.csrf.angular": "Angular",
|
||||
"form.security.item.csrf.cookie": "Cookie",
|
||||
"form.security.item.csrf.key": "Klucz",
|
||||
"form.security.item.csrf.secret": "Sekretny klucz",
|
||||
"form.security.item.hsts": "HOSTS",
|
||||
"form.security.item.hsts.includeSubDomains": "Subdomeny",
|
||||
"form.security.item.hsts.maxAge": "Max Age",
|
||||
@ -634,4 +629,4 @@
|
||||
"strapi.notification.success.languageAdd": "Język został pomyślnie dodany.",
|
||||
"strapi.notification.success.languageDelete": "Język został pomyślnie usunięty.",
|
||||
"strapi.notification.success.settingsEdit": "Ustawienia zostały pomyślnie zmienione."
|
||||
}
|
||||
}
|
||||
|
@ -54,11 +54,6 @@
|
||||
"form.security.description": "Defina as suas configurações de segurança.",
|
||||
"form.security.item.cors": "CORS",
|
||||
"form.security.item.cors.origin": "Origem",
|
||||
"form.security.item.csrf": "CSRF",
|
||||
"form.security.item.csrf.angular": "Angular",
|
||||
"form.security.item.csrf.cookie": "Cookie",
|
||||
"form.security.item.csrf.key": "Chave",
|
||||
"form.security.item.csrf.secret": "Segredo",
|
||||
"form.security.item.hsts": "HOSTS",
|
||||
"form.security.item.hsts.includeSubDomains": "Incluir Sub Domínio",
|
||||
"form.security.item.hsts.maxAge": "Idade máxima",
|
||||
@ -629,4 +624,4 @@
|
||||
"strapi.notification.success.languageAdd": "O idioma foi adicionado com sucesso.",
|
||||
"strapi.notification.success.languageDelete": "O idioma foi removido com sucesso.",
|
||||
"strapi.notification.success.settingsEdit": "As configurações foram atualizadas com sucesso."
|
||||
}
|
||||
}
|
||||
|
@ -54,11 +54,6 @@
|
||||
"form.security.description": "Configure as suas definições de segurança.",
|
||||
"form.security.item.cors": "Cors",
|
||||
"form.security.item.cors.origin": "Origem",
|
||||
"form.security.item.csrf": "CSRF",
|
||||
"form.security.item.csrf.angular": "Angular",
|
||||
"form.security.item.csrf.cookie": "Cookie",
|
||||
"form.security.item.csrf.key": "Chave",
|
||||
"form.security.item.csrf.secret": "Secreta",
|
||||
"form.security.item.hsts": "HOSTS",
|
||||
"form.security.item.hsts.includeSubDomains": "Incluir Sub Domínio",
|
||||
"form.security.item.hsts.maxAge": "Idade máxima",
|
||||
@ -634,4 +629,4 @@
|
||||
"strapi.notification.success.languageAdd": "O idioma foi adicionado com sucesso.",
|
||||
"strapi.notification.success.languageDelete": "O idioma foi excluido com sucesso.",
|
||||
"strapi.notification.success.settingsEdit": "As configurações foram actualizadas com sucesso."
|
||||
}
|
||||
}
|
||||
|
@ -54,11 +54,6 @@
|
||||
"form.security.description": "Задайте настройки безопасности.",
|
||||
"form.security.item.cors": "Cors",
|
||||
"form.security.item.cors.origin": "Origin",
|
||||
"form.security.item.csrf": "CSRF",
|
||||
"form.security.item.csrf.angular": "Angular",
|
||||
"form.security.item.csrf.cookie": "Cookie",
|
||||
"form.security.item.csrf.key": "Key",
|
||||
"form.security.item.csrf.secret": "Secret",
|
||||
"form.security.item.hsts": "HOSTS",
|
||||
"form.security.item.hsts.includeSubDomains": "Include Sub Domain",
|
||||
"form.security.item.hsts.maxAge": "Max Age",
|
||||
|
@ -54,11 +54,6 @@
|
||||
"form.security.description": "Güvenlik ayarlarınızı yapılandırın.",
|
||||
"form.security.item.cors": "Cors",
|
||||
"form.security.item.cors.origin": "Origin",
|
||||
"form.security.item.csrf": "CSRF",
|
||||
"form.security.item.csrf.angular": "Açısal",
|
||||
"form.security.item.csrf.cookie": "Çerez",
|
||||
"form.security.item.csrf.key": "Anahtar",
|
||||
"form.security.item.csrf.secret": "Gizli",
|
||||
"form.security.item.hsts": "HOSTS",
|
||||
"form.security.item.hsts.includeSubDomains": "Alt Etki Alanını Dahil Et",
|
||||
"form.security.item.hsts.maxAge": "Max Age",
|
||||
@ -634,4 +629,4 @@
|
||||
"strapi.notification.success.languageAdd": "Dil başarıyla eklendi.",
|
||||
"strapi.notification.success.languageDelete": "Dil başarıyla silindi.",
|
||||
"strapi.notification.success.settingsEdit": "Ayarlar başarıyla güncellendi."
|
||||
}
|
||||
}
|
||||
|
@ -53,11 +53,6 @@
|
||||
"form.security.description": "配置安全设置。",
|
||||
"form.security.item.cors": "Cors",
|
||||
"form.security.item.cors.origin": "Origin",
|
||||
"form.security.item.csrf": "CSRF",
|
||||
"form.security.item.csrf.angular": "Angular",
|
||||
"form.security.item.csrf.cookie": "Cookie",
|
||||
"form.security.item.csrf.key": "Key",
|
||||
"form.security.item.csrf.secret": "Secret",
|
||||
"form.security.item.hsts": "HOSTS",
|
||||
"form.security.item.hsts.includeSubDomains": "包括子域",
|
||||
"form.security.item.hsts.maxAge": "Max Age",
|
||||
@ -628,4 +623,4 @@
|
||||
"strapi.notification.success.languageAdd": "该语言已成功添加。",
|
||||
"strapi.notification.success.languageDelete": "数据库已被成功删除。",
|
||||
"strapi.notification.success.settingsEdit": "设置已成功更新。"
|
||||
}
|
||||
}
|
||||
|
@ -54,11 +54,6 @@
|
||||
"form.security.description": "調整安全性設定",
|
||||
"form.security.item.cors": "跨域資源共享 (Cors)",
|
||||
"form.security.item.cors.origin": "來源",
|
||||
"form.security.item.csrf": "跨站請求偽造保護 (CSRF)",
|
||||
"form.security.item.csrf.angular": "使用 Angular",
|
||||
"form.security.item.csrf.cookie": "Cookie",
|
||||
"form.security.item.csrf.key": "鍵值",
|
||||
"form.security.item.csrf.secret": "密鑰鍵值",
|
||||
"form.security.item.hsts": "強制安全傳輸 (HSTS)",
|
||||
"form.security.item.hsts.includeSubDomains": "包含子域名",
|
||||
"form.security.item.hsts.maxAge": "作用期",
|
||||
|
@ -285,70 +285,6 @@ module.exports = {
|
||||
name: 'form.security.name',
|
||||
description: 'form.security.description',
|
||||
sections: [
|
||||
{
|
||||
name: 'form.security.item.csrf',
|
||||
items: [
|
||||
{
|
||||
name: 'form.security.item.csrf.enabled',
|
||||
target: 'security.csrf.enabled',
|
||||
type: 'boolean',
|
||||
value: _.get(
|
||||
strapi.config,
|
||||
`environments.${env}.security.csrf.enabled`,
|
||||
null
|
||||
),
|
||||
items: [
|
||||
{
|
||||
name: 'form.security.item.csrf.key',
|
||||
target: 'security.csrf.key',
|
||||
type: 'string',
|
||||
value: _.get(
|
||||
strapi.config,
|
||||
`environments.${env}.security.csrf.key`,
|
||||
null
|
||||
),
|
||||
validations: {},
|
||||
},
|
||||
{
|
||||
name: 'form.security.item.csrf.secret',
|
||||
target: 'security.csrf.secret',
|
||||
type: 'string',
|
||||
value: _.get(
|
||||
strapi.config,
|
||||
`environments.${env}.security.csrf.secret`,
|
||||
null
|
||||
),
|
||||
validations: {},
|
||||
},
|
||||
{
|
||||
name: 'form.security.item.csrf.cookie',
|
||||
target: 'security.csrf.cookie',
|
||||
type: 'string',
|
||||
value: _.get(
|
||||
strapi.config,
|
||||
`environments.${env}.security.csrf.cookie`,
|
||||
null
|
||||
),
|
||||
validations: {},
|
||||
},
|
||||
{
|
||||
name: 'form.security.item.csrf.angular',
|
||||
target: 'security.csrf.angular',
|
||||
type: 'boolean',
|
||||
value: _.get(
|
||||
strapi.config,
|
||||
`environments.${env}.security.csrf.angular`,
|
||||
null
|
||||
),
|
||||
validations: {},
|
||||
},
|
||||
],
|
||||
validations: {
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'form.security.item.p3p',
|
||||
items: [
|
||||
|
@ -51,9 +51,7 @@ function* dataGet() {
|
||||
function* uploadFiles(action) {
|
||||
try {
|
||||
yield put(setLoading());
|
||||
const headers = {
|
||||
'X-Forwarded-Host': 'strapi',
|
||||
};
|
||||
const headers = {};
|
||||
const response = yield call(
|
||||
request,
|
||||
'/upload',
|
||||
|
@ -25,9 +25,17 @@ module.exports = {
|
||||
if (config.enabled === false) {
|
||||
return ctx.badRequest(
|
||||
null,
|
||||
ctx.request.admin
|
||||
? [{ messages: [{ id: 'Upload.status.disabled' }] }]
|
||||
: 'File upload is disabled'
|
||||
|
||||
[
|
||||
{
|
||||
messages: [
|
||||
{
|
||||
id: 'Upload.status.disabled',
|
||||
message: 'File upload is disabled',
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
@ -36,12 +44,11 @@ module.exports = {
|
||||
const { files = {} } = ctx.request.files || {};
|
||||
|
||||
if (_.isEmpty(files)) {
|
||||
return ctx.badRequest(
|
||||
null,
|
||||
ctx.request.admin
|
||||
? [{ messages: [{ id: 'Upload.status.empty' }] }]
|
||||
: 'Files are empty'
|
||||
);
|
||||
return ctx.badRequest(null, [
|
||||
{
|
||||
messages: [{ id: 'Upload.status.empty', message: 'Files are empty' }],
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
// Transform stream files to buffer
|
||||
@ -49,21 +56,17 @@ module.exports = {
|
||||
|
||||
const enhancedFiles = buffers.map(file => {
|
||||
if (file.size > config.sizeLimit) {
|
||||
return ctx.badRequest(
|
||||
null,
|
||||
ctx.request.admin
|
||||
? [
|
||||
{
|
||||
messages: [
|
||||
{
|
||||
id: 'Upload.status.sizeLimit',
|
||||
values: { file: file.name },
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
: `${file.name} file is bigger than limit size!`
|
||||
);
|
||||
return ctx.badRequest(null, [
|
||||
{
|
||||
messages: [
|
||||
{
|
||||
id: 'Upload.status.sizeLimit',
|
||||
message: `${file.name} file is bigger than limit size!`,
|
||||
values: { file: file.name },
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
// Add details to the file to be able to create the relationships.
|
||||
|
@ -153,7 +153,7 @@ describe('Upload plugin end to end tests', () => {
|
||||
|
||||
expect(res.statusCode).toBe(400);
|
||||
expect(res.body).toMatchObject({
|
||||
message: 'File upload is disabled',
|
||||
message: [{ messages: [{ message: 'File upload is disabled' }] }],
|
||||
});
|
||||
});
|
||||
|
||||
@ -164,7 +164,7 @@ describe('Upload plugin end to end tests', () => {
|
||||
|
||||
expect(res.statusCode).toBe(400);
|
||||
expect(res.body).toMatchObject({
|
||||
message: 'Files are empty',
|
||||
message: [{ messages: [{ message: 'Files are empty' }] }],
|
||||
});
|
||||
});
|
||||
|
||||
@ -181,7 +181,11 @@ describe('Upload plugin end to end tests', () => {
|
||||
|
||||
expect(res.statusCode).toBe(400);
|
||||
expect(res.body).toMatchObject({
|
||||
message: 'rec.jpg file is bigger than limit size!',
|
||||
message: [
|
||||
{
|
||||
messages: [{ message: 'rec.jpg file is bigger than limit size!' }],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -5,9 +5,16 @@ const lazyRateLimit = {
|
||||
};
|
||||
|
||||
module.exports = async (ctx, next) => {
|
||||
const message = ctx.request.admin
|
||||
? [{ messages: [{ id: 'Auth.form.error.ratelimit' }] }]
|
||||
: 'Too many attempts, please try again in a minute.';
|
||||
const message = [
|
||||
{
|
||||
messages: [
|
||||
{
|
||||
id: 'Auth.form.error.ratelimit',
|
||||
message: 'Too many attempts, please try again in a minute.',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
return lazyRateLimit.RateLimit.middleware(
|
||||
Object.assign(
|
||||
|
@ -10,8 +10,12 @@
|
||||
const crypto = require('crypto');
|
||||
const _ = require('lodash');
|
||||
const grant = require('grant-koa');
|
||||
const { sanitizeEntity } = require('strapi-utils');
|
||||
|
||||
const emailRegExp = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
||||
const formatError = error => [
|
||||
{ messages: [{ id: error.id, message: error.message, field: error.field }] },
|
||||
];
|
||||
|
||||
module.exports = {
|
||||
async callback(ctx) {
|
||||
@ -25,10 +29,7 @@ module.exports = {
|
||||
});
|
||||
|
||||
if (provider === 'local') {
|
||||
if (
|
||||
!_.get(await store.get({ key: 'grant' }), 'email.enabled') &&
|
||||
!ctx.request.admin
|
||||
) {
|
||||
if (!_.get(await store.get({ key: 'grant' }), 'email.enabled')) {
|
||||
return ctx.badRequest(null, 'This provider is disabled.');
|
||||
}
|
||||
|
||||
@ -36,9 +37,10 @@ module.exports = {
|
||||
if (!params.identifier) {
|
||||
return ctx.badRequest(
|
||||
null,
|
||||
ctx.request.admin
|
||||
? [{ messages: [{ id: 'Auth.form.error.email.provide' }] }]
|
||||
: 'Please provide your username or your e-mail.'
|
||||
formatError({
|
||||
id: 'Auth.form.error.email.provide',
|
||||
message: 'Please provide your username or your e-mail.',
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@ -46,9 +48,10 @@ module.exports = {
|
||||
if (!params.password) {
|
||||
return ctx.badRequest(
|
||||
null,
|
||||
ctx.request.admin
|
||||
? [{ messages: [{ id: 'Auth.form.error.password.provide' }] }]
|
||||
: 'Please provide your password.'
|
||||
formatError({
|
||||
id: 'Auth.form.error.password.provide',
|
||||
message: 'Please provide your password.',
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@ -72,9 +75,10 @@ module.exports = {
|
||||
if (!user) {
|
||||
return ctx.badRequest(
|
||||
null,
|
||||
ctx.request.admin
|
||||
? [{ messages: [{ id: 'Auth.form.error.invalid' }] }]
|
||||
: 'Identifier or password invalid.'
|
||||
formatError({
|
||||
id: 'Auth.form.error.invalid',
|
||||
message: 'Identifier or password invalid.',
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@ -84,18 +88,20 @@ module.exports = {
|
||||
) {
|
||||
return ctx.badRequest(
|
||||
null,
|
||||
ctx.request.admin
|
||||
? [{ messages: [{ id: 'Auth.form.error.confirmed' }] }]
|
||||
: 'Your account email is not confirmed.'
|
||||
formatError({
|
||||
id: 'Auth.form.error.confirmed',
|
||||
message: 'Your account email is not confirmed',
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (user.blocked === true) {
|
||||
return ctx.badRequest(
|
||||
null,
|
||||
ctx.request.admin
|
||||
? [{ messages: [{ id: 'Auth.form.error.blocked' }] }]
|
||||
: 'Your account has been blocked by the administrator.'
|
||||
formatError({
|
||||
id: 'Auth.form.error.blocked',
|
||||
message: 'Your account has been blocked by an administrator',
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@ -103,9 +109,11 @@ module.exports = {
|
||||
if (!user.password) {
|
||||
return ctx.badRequest(
|
||||
null,
|
||||
ctx.request.admin
|
||||
? [{ messages: [{ id: 'Auth.form.error.password.local' }] }]
|
||||
: 'This user never set a local password, please login thanks to the provider used during account creation.'
|
||||
formatError({
|
||||
id: 'Auth.form.error.password.local',
|
||||
message:
|
||||
'This user never set a local password, please login thanks to the provider used during account creation.',
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@ -116,24 +124,30 @@ module.exports = {
|
||||
if (!validPassword) {
|
||||
return ctx.badRequest(
|
||||
null,
|
||||
ctx.request.admin
|
||||
? [{ messages: [{ id: 'Auth.form.error.invalid' }] }]
|
||||
: 'Identifier or password invalid.'
|
||||
formatError({
|
||||
id: 'Auth.form.error.invalid',
|
||||
message: 'Identifier or password invalid.',
|
||||
})
|
||||
);
|
||||
} else {
|
||||
ctx.send({
|
||||
jwt: strapi.plugins['users-permissions'].services.jwt.issue({
|
||||
id: user.id,
|
||||
}),
|
||||
user: _.omit(user.toJSON ? user.toJSON() : user, [
|
||||
'password',
|
||||
'resetPasswordToken',
|
||||
]),
|
||||
user: sanitizeEntity(user.toJSON ? user.toJSON() : user, {
|
||||
model: strapi.query('user', 'users-permissions').model,
|
||||
}),
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (!_.get(await store.get({ key: 'grant' }), [provider, 'enabled'])) {
|
||||
return ctx.badRequest(null, 'This provider is disabled.');
|
||||
return ctx.badRequest(
|
||||
null,
|
||||
formatError({
|
||||
id: 'provider.disabled',
|
||||
message: 'This provider is disabled.',
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// Connect the user thanks to the third-party provider.
|
||||
@ -143,27 +157,20 @@ module.exports = {
|
||||
'users-permissions'
|
||||
].services.providers.connect(provider, ctx.query);
|
||||
} catch ([user, error]) {
|
||||
return ctx.badRequest(
|
||||
null,
|
||||
error === 'array' ? (ctx.request.admin ? error[0] : error[1]) : error
|
||||
);
|
||||
return ctx.badRequest(null, error === 'array' ? error[0] : error);
|
||||
}
|
||||
|
||||
if (!user) {
|
||||
return ctx.badRequest(
|
||||
null,
|
||||
error === 'array' ? (ctx.request.admin ? error[0] : error[1]) : error
|
||||
);
|
||||
return ctx.badRequest(null, error === 'array' ? error[0] : error);
|
||||
}
|
||||
|
||||
ctx.send({
|
||||
jwt: strapi.plugins['users-permissions'].services.jwt.issue({
|
||||
id: user.id,
|
||||
}),
|
||||
user: _.omit(user.toJSON ? user.toJSON() : user, [
|
||||
'password',
|
||||
'resetPasswordToken',
|
||||
]),
|
||||
user: sanitizeEntity(user.toJSON ? user.toJSON() : user, {
|
||||
model: strapi.query('user', 'users-permissions').model,
|
||||
}),
|
||||
});
|
||||
}
|
||||
},
|
||||
@ -184,9 +191,10 @@ module.exports = {
|
||||
if (!user) {
|
||||
return ctx.badRequest(
|
||||
null,
|
||||
ctx.request.admin
|
||||
? [{ messages: [{ id: 'Auth.form.error.code.provide' }] }]
|
||||
: 'Incorrect code provided.'
|
||||
formatError({
|
||||
id: 'Auth.form.error.code.provide',
|
||||
message: 'Incorrect code provided.',
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@ -206,10 +214,9 @@ module.exports = {
|
||||
jwt: strapi.plugins['users-permissions'].services.jwt.issue({
|
||||
id: user.id,
|
||||
}),
|
||||
user: _.omit(user.toJSON ? user.toJSON() : user, [
|
||||
'password',
|
||||
'resetPasswordToken',
|
||||
]),
|
||||
user: sanitizeEntity(user.toJSON ? user.toJSON() : user, {
|
||||
model: strapi.query('user', 'users-permissions').model,
|
||||
}),
|
||||
});
|
||||
} else if (
|
||||
params.password &&
|
||||
@ -218,16 +225,18 @@ module.exports = {
|
||||
) {
|
||||
return ctx.badRequest(
|
||||
null,
|
||||
ctx.request.admin
|
||||
? [{ messages: [{ id: 'Auth.form.error.password.matching' }] }]
|
||||
: 'Passwords do not match.'
|
||||
formatError({
|
||||
id: 'Auth.form.error.password.matching',
|
||||
message: 'Passwords do not match.',
|
||||
})
|
||||
);
|
||||
} else {
|
||||
return ctx.badRequest(
|
||||
null,
|
||||
ctx.request.admin
|
||||
? [{ messages: [{ id: 'Auth.form.error.params.provide' }] }]
|
||||
: 'Incorrect params provided.'
|
||||
formatError({
|
||||
id: 'Auth.form.error.params.provide',
|
||||
message: 'Incorrect params provided.',
|
||||
})
|
||||
);
|
||||
}
|
||||
},
|
||||
@ -270,9 +279,10 @@ module.exports = {
|
||||
if (!user) {
|
||||
return ctx.badRequest(
|
||||
null,
|
||||
ctx.request.admin
|
||||
? [{ messages: [{ id: 'Auth.form.error.user.not-exist' }] }]
|
||||
: 'This email does not exist.'
|
||||
formatError({
|
||||
id: 'Auth.form.error.user.not-exist',
|
||||
message: 'This email does not exist.',
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@ -353,9 +363,10 @@ module.exports = {
|
||||
if (!settings.allow_register) {
|
||||
return ctx.badRequest(
|
||||
null,
|
||||
ctx.request.admin
|
||||
? [{ messages: [{ id: 'Auth.advanced.allow_register' }] }]
|
||||
: 'Register action is currently disabled.'
|
||||
formatError({
|
||||
id: 'Auth.advanced.allow_register',
|
||||
message: 'Register action is currently disabled.',
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@ -367,9 +378,10 @@ module.exports = {
|
||||
if (!params.password) {
|
||||
return ctx.badRequest(
|
||||
null,
|
||||
ctx.request.admin
|
||||
? [{ messages: [{ id: 'Auth.form.error.password.provide' }] }]
|
||||
: 'Please provide your password.'
|
||||
formatError({
|
||||
id: 'Auth.form.error.password.provide',
|
||||
message: 'Please provide your password.',
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@ -377,9 +389,10 @@ module.exports = {
|
||||
if (!params.email) {
|
||||
return ctx.badRequest(
|
||||
null,
|
||||
ctx.request.admin
|
||||
? [{ messages: [{ id: 'Auth.form.error.email.provide' }] }]
|
||||
: 'Please provide your email.'
|
||||
formatError({
|
||||
id: 'Auth.form.error.email.provide',
|
||||
message: 'Please provide your email.',
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@ -392,9 +405,11 @@ module.exports = {
|
||||
) {
|
||||
return ctx.badRequest(
|
||||
null,
|
||||
ctx.request.admin
|
||||
? [{ messages: [{ id: 'Auth.form.error.password.format' }] }]
|
||||
: 'Your password cannot contain more than three times the symbol `$`.'
|
||||
formatError({
|
||||
id: 'Auth.form.error.password.format',
|
||||
message:
|
||||
'Your password cannot contain more than three times the symbol `$`.',
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@ -405,9 +420,10 @@ module.exports = {
|
||||
if (!role) {
|
||||
return ctx.badRequest(
|
||||
null,
|
||||
ctx.request.admin
|
||||
? [{ messages: [{ id: 'Auth.form.error.role.notFound' }] }]
|
||||
: 'Impossible to find the default role.'
|
||||
formatError({
|
||||
id: 'Auth.form.error.role.notFound',
|
||||
message: 'Impossible to find the default role.',
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@ -430,18 +446,20 @@ module.exports = {
|
||||
if (user && user.provider === params.provider) {
|
||||
return ctx.badRequest(
|
||||
null,
|
||||
ctx.request.admin
|
||||
? [{ messages: [{ id: 'Auth.form.error.email.taken' }] }]
|
||||
: 'Email is already taken.'
|
||||
formatError({
|
||||
id: 'Auth.form.error.email.taken',
|
||||
message: 'Email is already taken.',
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (user && user.provider !== params.provider && settings.unique_email) {
|
||||
return ctx.badRequest(
|
||||
null,
|
||||
ctx.request.admin
|
||||
? [{ messages: [{ id: 'Auth.form.error.email.taken' }] }]
|
||||
: 'Email is already taken.'
|
||||
formatError({
|
||||
id: 'Auth.form.error.email.taken',
|
||||
message: 'Email is already taken.',
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@ -515,20 +533,19 @@ module.exports = {
|
||||
|
||||
ctx.send({
|
||||
jwt,
|
||||
user: _.omit(user.toJSON ? user.toJSON() : user, [
|
||||
'password',
|
||||
'resetPasswordToken',
|
||||
]),
|
||||
user: sanitizeEntity(user.toJSON ? user.toJSON() : user, {
|
||||
model: strapi.query('user', 'users-permissions').model,
|
||||
}),
|
||||
});
|
||||
} catch (err) {
|
||||
const adminError = _.includes(err.message, 'username')
|
||||
? 'Auth.form.error.username.taken'
|
||||
: 'Auth.form.error.email.taken';
|
||||
? {
|
||||
id: 'Auth.form.error.username.taken',
|
||||
message: 'Username already taken',
|
||||
}
|
||||
: { id: 'Auth.form.error.email.taken', message: 'Email already taken' };
|
||||
|
||||
ctx.badRequest(
|
||||
null,
|
||||
ctx.request.admin ? [{ messages: [{ id: adminError }] }] : err.message
|
||||
);
|
||||
ctx.badRequest(null, formatError(adminError));
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -7,10 +7,15 @@
|
||||
*/
|
||||
|
||||
const _ = require('lodash');
|
||||
const { sanitizeEntity } = require('strapi-utils');
|
||||
|
||||
const sanitizeUser = user => _.omit(user, ['password', 'resetPasswordToken']);
|
||||
const adminError = error => [
|
||||
{ messages: [{ id: error.message, field: error.field }] },
|
||||
const sanitizeUser = user =>
|
||||
sanitizeEntity(user, {
|
||||
model: strapi.query('user', 'users-permissions').model,
|
||||
});
|
||||
|
||||
const formatError = error => [
|
||||
{ messages: [{ id: error.id, message: error.message, field: error.field }] },
|
||||
];
|
||||
|
||||
module.exports = {
|
||||
@ -99,12 +104,11 @@ module.exports = {
|
||||
if (userWithSameUsername) {
|
||||
return ctx.badRequest(
|
||||
null,
|
||||
ctx.request.admin
|
||||
? adminError({
|
||||
message: 'Auth.form.error.username.taken',
|
||||
field: ['username'],
|
||||
})
|
||||
: 'username.alreadyTaken.'
|
||||
formatError({
|
||||
id: 'Auth.form.error.username.taken',
|
||||
message: 'Username already taken.',
|
||||
field: ['username'],
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@ -116,12 +120,12 @@ module.exports = {
|
||||
if (userWithSameEmail) {
|
||||
return ctx.badRequest(
|
||||
null,
|
||||
ctx.request.admin
|
||||
? adminError({
|
||||
message: 'Auth.form.error.email.taken',
|
||||
field: ['email'],
|
||||
})
|
||||
: 'email.alreadyTaken'
|
||||
|
||||
formatError({
|
||||
id: 'Auth.form.error.email.taken',
|
||||
message: 'Email already taken.',
|
||||
field: ['email'],
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -146,10 +150,7 @@ module.exports = {
|
||||
|
||||
ctx.created(data);
|
||||
} catch (error) {
|
||||
ctx.badRequest(
|
||||
null,
|
||||
ctx.request.admin ? adminError(error) : error.message
|
||||
);
|
||||
ctx.badRequest(null, formatError(error));
|
||||
}
|
||||
},
|
||||
|
||||
@ -190,12 +191,11 @@ module.exports = {
|
||||
if (userWithSameUsername && userWithSameUsername.id != id) {
|
||||
return ctx.badRequest(
|
||||
null,
|
||||
ctx.request.admin
|
||||
? adminError({
|
||||
message: 'Auth.form.error.username.taken',
|
||||
field: ['username'],
|
||||
})
|
||||
: 'username.alreadyTaken.'
|
||||
formatError({
|
||||
id: 'Auth.form.error.username.taken',
|
||||
message: 'username.alreadyTaken.',
|
||||
field: ['username'],
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -208,12 +208,11 @@ module.exports = {
|
||||
if (userWithSameEmail && userWithSameEmail.id != id) {
|
||||
return ctx.badRequest(
|
||||
null,
|
||||
ctx.request.admin
|
||||
? adminError({
|
||||
message: 'Auth.form.error.email.taken',
|
||||
field: ['email'],
|
||||
})
|
||||
: 'email.alreadyTaken'
|
||||
formatError({
|
||||
id: 'Auth.form.error.email.taken',
|
||||
message: 'Eamil already taken',
|
||||
field: ['email'],
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,8 @@
|
||||
|
||||
const convertRestQueryParams = require('./convertRestQueryParams');
|
||||
const buildQuery = require('./buildQuery');
|
||||
const parseMultipartData = require('./parse-multipart');
|
||||
const sanitizeEntity = require('./sanitize-entity');
|
||||
|
||||
module.exports = {
|
||||
cli: require('./cli'),
|
||||
@ -21,4 +23,6 @@ module.exports = {
|
||||
templateConfiguration: require('./templateConfiguration'),
|
||||
convertRestQueryParams,
|
||||
buildQuery,
|
||||
parseMultipartData,
|
||||
sanitizeEntity,
|
||||
};
|
||||
|
38
packages/strapi-utils/lib/sanitize-entity.js
Normal file
38
packages/strapi-utils/lib/sanitize-entity.js
Normal file
@ -0,0 +1,38 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = function sanitizeEntity(data, { model, withPrivate = false }) {
|
||||
if (typeof data !== 'object' || data == null) return data;
|
||||
|
||||
let plainData = typeof data.toJSON === 'function' ? data.toJSON() : data;
|
||||
|
||||
const attributes = model.attributes;
|
||||
return Object.keys(plainData).reduce((acc, key) => {
|
||||
const attribute = attributes[key];
|
||||
if (attribute && attribute.private === true && withPrivate !== true) {
|
||||
return acc;
|
||||
}
|
||||
|
||||
if (
|
||||
attribute &&
|
||||
(attribute.model || attribute.collection || attribute.type === 'group')
|
||||
) {
|
||||
const targetName =
|
||||
attribute.model || attribute.collection || attribute.group;
|
||||
|
||||
const targetModel = strapi.getModel(targetName, attribute.plugin);
|
||||
|
||||
if (targetModel && plainData[key] !== null) {
|
||||
acc[key] = Array.isArray(plainData[key])
|
||||
? plainData[key].map(entity =>
|
||||
sanitizeEntity(entity, { model: targetModel, withPrivate })
|
||||
)
|
||||
: sanitizeEntity(plainData[key], { model: targetModel, withPrivate });
|
||||
|
||||
return acc;
|
||||
}
|
||||
}
|
||||
|
||||
acc[key] = plainData[key];
|
||||
return acc;
|
||||
}, {});
|
||||
};
|
@ -1,17 +1,13 @@
|
||||
'use strict';
|
||||
|
||||
const parseMultipartData = require('./utils/parse-multipart');
|
||||
|
||||
const proto = {
|
||||
parseMultipartData,
|
||||
};
|
||||
const { parseMultipartData, sanitizeEntity } = require('strapi-utils');
|
||||
|
||||
/**
|
||||
* default bookshelf controller
|
||||
*
|
||||
*/
|
||||
module.exports = ({ service }) => {
|
||||
return Object.assign(Object.create(proto), {
|
||||
module.exports = ({ service, model }) => {
|
||||
return {
|
||||
/**
|
||||
* expose some utils so the end users can use them
|
||||
*/
|
||||
@ -22,11 +18,15 @@ module.exports = ({ service }) => {
|
||||
* @return {Object|Array}
|
||||
*/
|
||||
|
||||
find(ctx) {
|
||||
async find(ctx) {
|
||||
let entities;
|
||||
if (ctx.query._q) {
|
||||
return service.search(ctx.query);
|
||||
entities = await service.search(ctx.query);
|
||||
} else {
|
||||
entities = await service.find(ctx.query);
|
||||
}
|
||||
return service.find(ctx.query);
|
||||
|
||||
return entities.map(entity => sanitizeEntity(entity, { model }));
|
||||
},
|
||||
|
||||
/**
|
||||
@ -35,8 +35,9 @@ module.exports = ({ service }) => {
|
||||
* @return {Object}
|
||||
*/
|
||||
|
||||
findOne(ctx) {
|
||||
return service.findOne(ctx.params);
|
||||
async findOne(ctx) {
|
||||
const entity = await service.findOne(ctx.params);
|
||||
return sanitizeEntity(entity, { model });
|
||||
},
|
||||
|
||||
/**
|
||||
@ -58,13 +59,15 @@ module.exports = ({ service }) => {
|
||||
* @return {Object}
|
||||
*/
|
||||
|
||||
create(ctx) {
|
||||
async create(ctx) {
|
||||
let entity;
|
||||
if (ctx.is('multipart')) {
|
||||
const { data, files } = this.parseMultipartData(ctx);
|
||||
return service.create(data, { files });
|
||||
const { data, files } = parseMultipartData(ctx);
|
||||
entity = await service.create(data, { files });
|
||||
} else {
|
||||
entity = await service.create(ctx.request.body);
|
||||
}
|
||||
|
||||
return service.create(ctx.request.body);
|
||||
return sanitizeEntity(entity, { model });
|
||||
},
|
||||
|
||||
/**
|
||||
@ -73,13 +76,16 @@ module.exports = ({ service }) => {
|
||||
* @return {Object}
|
||||
*/
|
||||
|
||||
update(ctx) {
|
||||
async update(ctx) {
|
||||
let entity;
|
||||
if (ctx.is('multipart')) {
|
||||
const { data, files } = this.parseMultipartData(ctx);
|
||||
return service.update(ctx.params, data, { files });
|
||||
const { data, files } = parseMultipartData(ctx);
|
||||
entity = await service.update(ctx.params, data, { files });
|
||||
} else {
|
||||
entity = await service.update(ctx.params, ctx.request.body);
|
||||
}
|
||||
|
||||
return service.update(ctx.params, ctx.request.body);
|
||||
return sanitizeEntity(entity, { model });
|
||||
},
|
||||
|
||||
/**
|
||||
@ -88,8 +94,9 @@ module.exports = ({ service }) => {
|
||||
* @return {Object}
|
||||
*/
|
||||
|
||||
delete(ctx) {
|
||||
return service.delete(ctx.params);
|
||||
async delete(ctx) {
|
||||
const entity = await service.delete(ctx.params);
|
||||
return sanitizeEntity(entity, { model });
|
||||
},
|
||||
});
|
||||
};
|
||||
};
|
||||
|
6
packages/strapi/lib/core/bootstrap.js
vendored
6
packages/strapi/lib/core/bootstrap.js
vendored
@ -77,7 +77,7 @@ module.exports = function(strapi) {
|
||||
);
|
||||
|
||||
const controller = Object.assign(
|
||||
createController({ service }),
|
||||
createController({ service, model }),
|
||||
userController,
|
||||
{ identity: userController.identity || _.upperFirst(index) }
|
||||
);
|
||||
@ -209,10 +209,6 @@ module.exports = function(strapi) {
|
||||
boom: {
|
||||
enabled: true,
|
||||
},
|
||||
mask: {
|
||||
enabled: true,
|
||||
},
|
||||
// Necessary middlewares for the administration panel.
|
||||
cors: {
|
||||
enabled: true,
|
||||
},
|
||||
|
@ -10,13 +10,7 @@ const defaults = {
|
||||
maxAge: 31536000,
|
||||
credentials: true,
|
||||
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'],
|
||||
headers: [
|
||||
'Content-Type',
|
||||
'Authorization',
|
||||
'Origin',
|
||||
'Accept',
|
||||
'X-Forwarded-Host',
|
||||
],
|
||||
headers: ['Content-Type', 'Authorization', 'Origin', 'Accept'],
|
||||
keepHeadersOnError: false,
|
||||
};
|
||||
|
||||
|
@ -1,7 +0,0 @@
|
||||
{
|
||||
"csrf": {
|
||||
"enabled": false,
|
||||
"key": "_csrf",
|
||||
"secret": "_csrfSecret"
|
||||
}
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies
|
||||
*/
|
||||
const convert = require('koa-convert');
|
||||
const { csrf } = require('koa-lusca');
|
||||
|
||||
/**
|
||||
* CSRF hook
|
||||
*/
|
||||
|
||||
module.exports = strapi => {
|
||||
return {
|
||||
/**
|
||||
* Initialize the hook
|
||||
*/
|
||||
|
||||
initialize() {
|
||||
strapi.app.use(async (ctx, next) => {
|
||||
if (ctx.request.admin) return await next();
|
||||
|
||||
return await convert(csrf(strapi.config.middleware.settings.csrf))(
|
||||
ctx,
|
||||
next
|
||||
);
|
||||
});
|
||||
},
|
||||
};
|
||||
};
|
@ -3,17 +3,6 @@
|
||||
const { uniq, difference, get, isUndefined, merge } = require('lodash');
|
||||
|
||||
module.exports = async function() {
|
||||
// Set if is admin destination for middleware application.
|
||||
this.app.use(async (ctx, next) => {
|
||||
if (ctx.request.header['origin'] === 'http://localhost:4000') {
|
||||
ctx.request.header['x-forwarded-host'] = 'strapi';
|
||||
}
|
||||
|
||||
ctx.request.admin = ctx.request.header['x-forwarded-host'] === 'strapi';
|
||||
|
||||
await next();
|
||||
});
|
||||
|
||||
/** Utils */
|
||||
|
||||
const middlewareConfig = this.config.middleware;
|
||||
|
@ -1,5 +0,0 @@
|
||||
{
|
||||
"mask": {
|
||||
"enabled": false
|
||||
}
|
||||
}
|
@ -1,189 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies
|
||||
*/
|
||||
|
||||
/**
|
||||
* Mask filter middleware
|
||||
*/
|
||||
|
||||
const _ = require('lodash');
|
||||
|
||||
module.exports = strapi => {
|
||||
return {
|
||||
/**
|
||||
* Initialize the hook
|
||||
*/
|
||||
|
||||
initialize() {
|
||||
// Enable the middleware if we need it.
|
||||
const enabled = (() => {
|
||||
const main = Object.keys(strapi.models).reduce((acc, current) => {
|
||||
if (
|
||||
Object.values(strapi.models[current].attributes).find(
|
||||
attr => attr.private === true
|
||||
)
|
||||
) {
|
||||
acc = true;
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, false);
|
||||
|
||||
const plugins = Object.keys(strapi.plugins).reduce((acc, plugin) => {
|
||||
const bool = Object.keys(strapi.plugins[plugin].models).reduce(
|
||||
(acc, model) => {
|
||||
if (
|
||||
Object.values(
|
||||
strapi.plugins[plugin].models[model].attributes
|
||||
).find(attr => attr.private === true)
|
||||
) {
|
||||
acc = true;
|
||||
}
|
||||
|
||||
return acc;
|
||||
},
|
||||
false
|
||||
);
|
||||
|
||||
if (bool) {
|
||||
acc = true;
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, false);
|
||||
|
||||
return main || plugins;
|
||||
})();
|
||||
|
||||
if (enabled) {
|
||||
strapi.app.use(async (ctx, next) => {
|
||||
// Execute next middleware.
|
||||
await next();
|
||||
|
||||
// Recursive to mask the private properties.
|
||||
const mask = payload => {
|
||||
// Handle ORM toJSON() method to work on real JSON object.
|
||||
payload = payload && payload.toJSON ? payload.toJSON() : payload;
|
||||
|
||||
if (_.isArray(payload)) {
|
||||
return payload.map(mask);
|
||||
} else if (_.isPlainObject(payload)) {
|
||||
return this.mask(
|
||||
ctx,
|
||||
Object.keys(payload).reduce((acc, current) => {
|
||||
acc[current] = _.isObjectLike(payload[current])
|
||||
? mask(payload[current])
|
||||
: payload[current];
|
||||
|
||||
return acc;
|
||||
}, {})
|
||||
);
|
||||
}
|
||||
|
||||
return payload;
|
||||
};
|
||||
|
||||
// Only pick successful JSON requests.
|
||||
if (
|
||||
[200, 201, 202].includes(ctx.status) &&
|
||||
ctx.type === 'application/json' &&
|
||||
!ctx.request.admin
|
||||
) {
|
||||
ctx.body = mask(ctx.body);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
mask(ctx, value) {
|
||||
const models = this.filteredModels(
|
||||
this.whichModels(value, ctx.request.route.plugin)
|
||||
);
|
||||
|
||||
if (models.length === 0) {
|
||||
return value;
|
||||
}
|
||||
|
||||
const attributesToHide = models.reduce((acc, match) => {
|
||||
const attributes = match.plugin
|
||||
? strapi.plugins[match.plugin].models[match.model].attributes
|
||||
: strapi.models[match.model].attributes;
|
||||
|
||||
acc = acc.concat(
|
||||
Object.keys(attributes).filter(
|
||||
attr => attributes[attr].private === true
|
||||
)
|
||||
);
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
// Hide attribute.
|
||||
return _.omit(value, attributesToHide);
|
||||
},
|
||||
|
||||
whichModels(value) {
|
||||
const keys = Object.keys(value);
|
||||
let maxMatch = 0;
|
||||
let matchs = [];
|
||||
|
||||
const match = (model, plugin) => {
|
||||
const attributes = plugin
|
||||
? Object.keys(strapi.plugins[plugin].models[model].attributes)
|
||||
: Object.keys(strapi.models[model].attributes);
|
||||
|
||||
const intersection = _.intersection(
|
||||
keys,
|
||||
attributes.filter(attr => ['id', '_id', '_v'].indexOf(attr) === -1)
|
||||
).length;
|
||||
|
||||
// Most matched model.
|
||||
if (intersection > maxMatch) {
|
||||
maxMatch = intersection;
|
||||
matchs = [
|
||||
{
|
||||
plugin,
|
||||
model,
|
||||
intersection,
|
||||
},
|
||||
];
|
||||
} else if (intersection === maxMatch && intersection > 0) {
|
||||
matchs.push({
|
||||
plugin,
|
||||
model,
|
||||
intersection,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Application models.
|
||||
Object.keys(strapi.models).forEach(model => match(model));
|
||||
// Plugins models.
|
||||
Object.keys(strapi.plugins).forEach(plugin => {
|
||||
Object.keys(strapi.plugins[plugin].models).forEach(model =>
|
||||
match(model, plugin)
|
||||
);
|
||||
});
|
||||
|
||||
return matchs;
|
||||
},
|
||||
|
||||
filteredModels(matchs) {
|
||||
return matchs.reduce((acc, match, index) => {
|
||||
const attributes = match.plugin
|
||||
? strapi.plugins[match.plugin].models[match.model].attributes
|
||||
: strapi.models[match.model].attributes;
|
||||
|
||||
// Filtered model which have more than 50% of the attributes
|
||||
// in common with the original model.
|
||||
if (match.intersection >= Object.keys(attributes).length / 2) {
|
||||
acc[index] = match;
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
},
|
||||
};
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user