mirror of
https://github.com/strapi/strapi.git
synced 2025-09-25 16:29:34 +00:00
Merge branch 'master' into design/icons
This commit is contained in:
commit
36f9f27d0c
@ -33,6 +33,23 @@ The following types are currently available:
|
|||||||
- `json`
|
- `json`
|
||||||
- `email`
|
- `email`
|
||||||
|
|
||||||
|
#### Validations
|
||||||
|
|
||||||
|
You can apply basic validations to the attributes. The following supported validations are *only supported by MongoDB* connection.
|
||||||
|
If you're using SQL databases, you should use the native SQL constraints to apply them.
|
||||||
|
|
||||||
|
- `required` (boolean) — if true adds a required validator for this property.
|
||||||
|
- `unique` (boolean) — whether to define a unique index on this property.
|
||||||
|
- `max` (integer) — checks if the value is greater than or equal to the given minimum.
|
||||||
|
- `min` (integer) — checks if the value is less than or equal to the given maximum.
|
||||||
|
|
||||||
|
|
||||||
|
**Security validations**
|
||||||
|
To improve the Developer eXperience when developing or using the administration panel, the framework enhances the attributes with these "security validations":
|
||||||
|
|
||||||
|
- `private` (boolean) — if true, the attribute will be removed from the server response (it's useful to hide sensitive data).
|
||||||
|
- `configurable` (boolean) - if false, the attribute isn't configurable from the Content Type Builder plugin.
|
||||||
|
|
||||||
#### Example
|
#### Example
|
||||||
|
|
||||||
**Path —** `User.settings.json`.
|
**Path —** `User.settings.json`.
|
||||||
@ -50,14 +67,23 @@ The following types are currently available:
|
|||||||
"lastname": {
|
"lastname": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"email": {
|
||||||
|
"type": "email",
|
||||||
|
"required": true,
|
||||||
|
"unique": true
|
||||||
|
},
|
||||||
"password": {
|
"password": {
|
||||||
"type": "password"
|
"type": "password",
|
||||||
|
"required": true,
|
||||||
|
"private": true
|
||||||
},
|
},
|
||||||
"about": {
|
"about": {
|
||||||
"type": "description"
|
"type": "description"
|
||||||
},
|
},
|
||||||
"age": {
|
"age": {
|
||||||
"type": "integer"
|
"type": "integer",
|
||||||
|
"min": 18,
|
||||||
|
"max": 99
|
||||||
},
|
},
|
||||||
"birthday": {
|
"birthday": {
|
||||||
"type": "date"
|
"type": "date"
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"timeout": 1000,
|
"timeout": 3000,
|
||||||
"load": {
|
"load": {
|
||||||
"order": [
|
"order": [
|
||||||
"Define the hooks' load order by putting their names in this array in the right order"
|
"Define the hooks' load order by putting their names in this array in the right order"
|
||||||
|
@ -8,7 +8,6 @@
|
|||||||
|
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const crypto = require('crypto');
|
const crypto = require('crypto');
|
||||||
const Grant = require('grant-koa');
|
|
||||||
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 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,}))$/;
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
@ -137,6 +136,7 @@ module.exports = {
|
|||||||
return ctx.badRequest(null, 'This provider is disabled.');
|
return ctx.badRequest(null, 'This provider is disabled.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Grant = require('grant-koa');
|
||||||
const grant = new Grant(strapi.plugins['users-permissions'].config.grant);
|
const grant = new Grant(strapi.plugins['users-permissions'].config.grant);
|
||||||
|
|
||||||
return strapi.koaMiddlewares.compose(grant.middleware)(ctx, next);
|
return strapi.koaMiddlewares.compose(grant.middleware)(ctx, next);
|
||||||
|
@ -25,11 +25,13 @@
|
|||||||
"password": {
|
"password": {
|
||||||
"type": "password",
|
"type": "password",
|
||||||
"minLength": 6,
|
"minLength": 6,
|
||||||
"configurable": false
|
"configurable": false,
|
||||||
|
"private": true
|
||||||
},
|
},
|
||||||
"resetPasswordToken": {
|
"resetPasswordToken": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"configurable": false
|
"configurable": false,
|
||||||
|
"private": true
|
||||||
},
|
},
|
||||||
"role": {
|
"role": {
|
||||||
"model": "role",
|
"model": "role",
|
||||||
|
@ -244,6 +244,9 @@ module.exports.app = async function() {
|
|||||||
boom: {
|
boom: {
|
||||||
enabled: true
|
enabled: true
|
||||||
},
|
},
|
||||||
|
mask: {
|
||||||
|
enabled: true
|
||||||
|
},
|
||||||
// Necessary middlewares for the administration panel.
|
// Necessary middlewares for the administration panel.
|
||||||
cors: {
|
cors: {
|
||||||
enabled: true
|
enabled: true
|
||||||
|
5
packages/strapi/lib/middlewares/mask/defaults.json
Normal file
5
packages/strapi/lib/middlewares/mask/defaults.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"mask": {
|
||||||
|
"enabled": false
|
||||||
|
}
|
||||||
|
}
|
158
packages/strapi/lib/middlewares/mask/index.js
Normal file
158
packages/strapi/lib/middlewares/mask/index.js
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Module dependencies
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mask filter middleware
|
||||||
|
*/
|
||||||
|
|
||||||
|
const _ = require('lodash');
|
||||||
|
|
||||||
|
module.exports = strapi => {
|
||||||
|
return {
|
||||||
|
/**
|
||||||
|
* Initialize the hook
|
||||||
|
*/
|
||||||
|
|
||||||
|
initialize: function (cb) {
|
||||||
|
// 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) => {
|
||||||
|
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.body = mask(ctx.body);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
cb();
|
||||||
|
},
|
||||||
|
|
||||||
|
mask: function (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: function (value, plugin) {
|
||||||
|
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: function (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