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`
|
||||
- `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
|
||||
|
||||
**Path —** `User.settings.json`.
|
||||
@ -50,14 +67,23 @@ The following types are currently available:
|
||||
"lastname": {
|
||||
"type": "string"
|
||||
},
|
||||
"email": {
|
||||
"type": "email",
|
||||
"required": true,
|
||||
"unique": true
|
||||
},
|
||||
"password": {
|
||||
"type": "password"
|
||||
"type": "password",
|
||||
"required": true,
|
||||
"private": true
|
||||
},
|
||||
"about": {
|
||||
"type": "description"
|
||||
},
|
||||
"age": {
|
||||
"type": "integer"
|
||||
"type": "integer",
|
||||
"min": 18,
|
||||
"max": 99
|
||||
},
|
||||
"birthday": {
|
||||
"type": "date"
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"timeout": 1000,
|
||||
"timeout": 3000,
|
||||
"load": {
|
||||
"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 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,}))$/;
|
||||
|
||||
module.exports = {
|
||||
@ -137,6 +136,7 @@ module.exports = {
|
||||
return ctx.badRequest(null, 'This provider is disabled.');
|
||||
}
|
||||
|
||||
const Grant = require('grant-koa');
|
||||
const grant = new Grant(strapi.plugins['users-permissions'].config.grant);
|
||||
|
||||
return strapi.koaMiddlewares.compose(grant.middleware)(ctx, next);
|
||||
|
@ -25,11 +25,13 @@
|
||||
"password": {
|
||||
"type": "password",
|
||||
"minLength": 6,
|
||||
"configurable": false
|
||||
"configurable": false,
|
||||
"private": true
|
||||
},
|
||||
"resetPasswordToken": {
|
||||
"type": "string",
|
||||
"configurable": false
|
||||
"configurable": false,
|
||||
"private": true
|
||||
},
|
||||
"role": {
|
||||
"model": "role",
|
||||
|
@ -244,6 +244,9 @@ module.exports.app = async function() {
|
||||
boom: {
|
||||
enabled: true
|
||||
},
|
||||
mask: {
|
||||
enabled: true
|
||||
},
|
||||
// Necessary middlewares for the administration panel.
|
||||
cors: {
|
||||
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