| 
									
										
										
										
											2019-08-21 12:10:23 +02:00
										 |  |  | 'use strict'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-01 18:08:21 +02:00
										 |  |  | const _ = require('lodash'); | 
					
						
							| 
									
										
										
										
											2021-03-12 14:34:04 +01:00
										 |  |  | const { | 
					
						
							|  |  |  |   constants, | 
					
						
							|  |  |  |   isPrivateAttribute, | 
					
						
							|  |  |  |   getNonWritableAttributes, | 
					
						
							|  |  |  |   getNonVisibleAttributes, | 
					
						
							|  |  |  |   getWritableAttributes, | 
					
						
							|  |  |  | } = require('./content-types'); | 
					
						
							| 
									
										
										
										
											2020-10-27 11:27:17 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-08 21:53:30 +02:00
										 |  |  | const { ID_ATTRIBUTE, CREATED_AT_ATTRIBUTE, UPDATED_AT_ATTRIBUTE } = constants; | 
					
						
							| 
									
										
										
										
											2019-08-21 12:10:23 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-01 18:08:21 +02:00
										 |  |  | const sanitizeEntity = (dataSource, options) => { | 
					
						
							|  |  |  |   const { model, withPrivate = false, isOutput = true, includeFields = null } = options; | 
					
						
							| 
									
										
										
										
											2019-09-20 09:56:00 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-01 18:08:21 +02:00
										 |  |  |   if (typeof dataSource !== 'object' || _.isNil(dataSource)) { | 
					
						
							|  |  |  |     return dataSource; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2019-10-04 11:38:54 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-01 18:08:21 +02:00
										 |  |  |   const data = parseOriginalData(dataSource); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-18 16:26:24 +02:00
										 |  |  |   if (typeof data !== 'object' || _.isNil(data)) { | 
					
						
							| 
									
										
										
										
											2020-07-01 18:08:21 +02:00
										 |  |  |     return data; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-18 16:26:24 +02:00
										 |  |  |   if (_.isArray(data)) { | 
					
						
							|  |  |  |     return data.map(entity => sanitizeEntity(entity, options)); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-01 18:08:21 +02:00
										 |  |  |   if (_.isNil(model)) { | 
					
						
							| 
									
										
										
										
											2020-11-03 17:19:43 +01:00
										 |  |  |     if (isOutput) { | 
					
						
							|  |  |  |       return null; | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       return data; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-07-01 18:08:21 +02:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const { attributes } = model; | 
					
						
							|  |  |  |   const allowedFields = getAllowedFields({ includeFields, model, isOutput }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const reducerFn = (acc, value, key) => { | 
					
						
							| 
									
										
										
										
											2019-08-21 12:10:23 +02:00
										 |  |  |     const attribute = attributes[key]; | 
					
						
							| 
									
										
										
										
											2020-07-01 18:08:21 +02:00
										 |  |  |     const allowedFieldsHasKey = allowedFields.includes(key); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-24 02:40:10 -05:00
										 |  |  |     if (shouldRemoveAttribute(model, key, attribute, { withPrivate, isOutput })) { | 
					
						
							| 
									
										
										
										
											2019-08-21 12:10:23 +02:00
										 |  |  |       return acc; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-01 18:08:21 +02:00
										 |  |  |     // Relations
 | 
					
						
							|  |  |  |     const relation = attribute && (attribute.model || attribute.collection || attribute.component); | 
					
						
							| 
									
										
										
										
											2020-07-08 13:53:56 +02:00
										 |  |  |     if (relation) { | 
					
						
							|  |  |  |       if (_.isNil(value)) { | 
					
						
							|  |  |  |         return { ...acc, [key]: value }; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-02 19:26:36 +02:00
										 |  |  |       const [nextFields, isAllowed] = includeFields | 
					
						
							|  |  |  |         ? getNextFields(allowedFields, key, { allowedFieldsHasKey }) | 
					
						
							|  |  |  |         : [null, true]; | 
					
						
							| 
									
										
										
										
											2019-08-21 12:10:23 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-01 18:08:21 +02:00
										 |  |  |       if (!isAllowed) { | 
					
						
							| 
									
										
										
										
											2019-08-21 12:10:23 +02:00
										 |  |  |         return acc; | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2020-07-01 18:08:21 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-03 19:28:11 +01:00
										 |  |  |       const baseOptions = { | 
					
						
							| 
									
										
										
										
											2020-07-01 18:08:21 +02:00
										 |  |  |         withPrivate, | 
					
						
							|  |  |  |         isOutput, | 
					
						
							|  |  |  |         includeFields: nextFields, | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-03 19:28:11 +01:00
										 |  |  |       let sanitizeFn; | 
					
						
							|  |  |  |       if (relation === '*') { | 
					
						
							| 
									
										
										
										
											2021-02-18 11:20:32 +01:00
										 |  |  |         sanitizeFn = entity => { | 
					
						
							|  |  |  |           if (_.isNil(entity) || !_.has(entity, '__contentType')) { | 
					
						
							|  |  |  |             return entity; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           return sanitizeEntity(entity, { | 
					
						
							| 
									
										
										
										
											2021-07-19 12:39:43 +02:00
										 |  |  |             model: strapi.getModel(entity.__contentType), | 
					
						
							| 
									
										
										
										
											2021-02-03 19:28:11 +01:00
										 |  |  |             ...baseOptions, | 
					
						
							|  |  |  |           }); | 
					
						
							| 
									
										
										
										
											2021-02-18 11:20:32 +01:00
										 |  |  |         }; | 
					
						
							| 
									
										
										
										
											2021-02-03 19:28:11 +01:00
										 |  |  |       } else { | 
					
						
							|  |  |  |         sanitizeFn = entity => | 
					
						
							|  |  |  |           sanitizeEntity(entity, { | 
					
						
							|  |  |  |             model: strapi.getModel(relation, attribute.plugin), | 
					
						
							|  |  |  |             ...baseOptions, | 
					
						
							|  |  |  |           }); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       const nextVal = Array.isArray(value) ? value.map(sanitizeFn) : sanitizeFn(value); | 
					
						
							| 
									
										
										
										
											2020-07-01 18:08:21 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |       return { ...acc, [key]: nextVal }; | 
					
						
							| 
									
										
										
										
											2019-08-21 12:10:23 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-06 11:19:58 +02:00
										 |  |  |     const isAllowedField = !includeFields || allowedFieldsHasKey; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-01 18:08:21 +02:00
										 |  |  |     // Dynamic zones
 | 
					
						
							| 
									
										
										
										
											2020-08-06 11:19:58 +02:00
										 |  |  |     if (attribute && attribute.type === 'dynamiczone' && value !== null && isAllowedField) { | 
					
						
							| 
									
										
										
										
											2020-07-01 18:08:21 +02:00
										 |  |  |       const nextVal = value.map(elem => | 
					
						
							|  |  |  |         sanitizeEntity(elem, { | 
					
						
							|  |  |  |           model: strapi.getModel(elem.__component), | 
					
						
							|  |  |  |           withPrivate, | 
					
						
							|  |  |  |           isOutput, | 
					
						
							|  |  |  |         }) | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |       return { ...acc, [key]: nextVal }; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-08-06 11:19:58 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-01 18:08:21 +02:00
										 |  |  |     // Other fields
 | 
					
						
							|  |  |  |     if (isAllowedField) { | 
					
						
							|  |  |  |       return { ...acc, [key]: value }; | 
					
						
							| 
									
										
										
										
											2020-07-01 13:03:30 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-21 12:10:23 +02:00
										 |  |  |     return acc; | 
					
						
							| 
									
										
										
										
											2020-07-01 18:08:21 +02:00
										 |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return _.reduce(data, reducerFn, {}); | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const parseOriginalData = data => (_.isFunction(data.toJSON) ? data.toJSON() : data); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-08 16:07:11 +02:00
										 |  |  | const COMPONENT_FIELDS = ['__component']; | 
					
						
							| 
									
										
										
										
											2021-06-30 20:00:03 +02:00
										 |  |  | const STATIC_FIELDS = [ID_ATTRIBUTE]; | 
					
						
							| 
									
										
										
										
											2020-07-08 16:07:11 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-01 18:08:21 +02:00
										 |  |  | const getAllowedFields = ({ includeFields, model, isOutput }) => { | 
					
						
							| 
									
										
										
										
											2021-03-09 20:50:23 +01:00
										 |  |  |   const nonWritableAttributes = getNonWritableAttributes(model); | 
					
						
							| 
									
										
										
										
											2021-03-12 14:34:04 +01:00
										 |  |  |   const nonVisibleAttributes = getNonVisibleAttributes(model); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const writableAttributes = getWritableAttributes(model); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const nonVisibleWritableAttributes = _.intersection(writableAttributes, nonVisibleAttributes); | 
					
						
							| 
									
										
										
										
											2020-07-01 18:08:21 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |   return _.concat( | 
					
						
							|  |  |  |     includeFields || [], | 
					
						
							|  |  |  |     ...(isOutput | 
					
						
							| 
									
										
										
										
											2020-08-18 17:09:21 +02:00
										 |  |  |       ? [ | 
					
						
							|  |  |  |           STATIC_FIELDS, | 
					
						
							| 
									
										
										
										
											2021-07-08 21:53:30 +02:00
										 |  |  |           CREATED_AT_ATTRIBUTE, | 
					
						
							|  |  |  |           UPDATED_AT_ATTRIBUTE, | 
					
						
							| 
									
										
										
										
											2020-08-18 17:09:21 +02:00
										 |  |  |           COMPONENT_FIELDS, | 
					
						
							| 
									
										
										
										
											2021-03-09 20:50:23 +01:00
										 |  |  |           ...nonWritableAttributes, | 
					
						
							| 
									
										
										
										
											2021-03-12 14:34:04 +01:00
										 |  |  |           ...nonVisibleAttributes, | 
					
						
							| 
									
										
										
										
											2020-08-18 17:09:21 +02:00
										 |  |  |         ] | 
					
						
							| 
									
										
										
										
											2021-06-30 20:00:03 +02:00
										 |  |  |       : [STATIC_FIELDS, COMPONENT_FIELDS, ...nonVisibleWritableAttributes]) | 
					
						
							| 
									
										
										
										
											2020-07-01 18:08:21 +02:00
										 |  |  |   ); | 
					
						
							| 
									
										
										
										
											2019-08-21 12:10:23 +02:00
										 |  |  | }; | 
					
						
							| 
									
										
										
										
											2020-07-01 18:08:21 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | const getNextFields = (fields, key, { allowedFieldsHasKey }) => { | 
					
						
							|  |  |  |   const searchStr = `${key}.`; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const transformedFields = (fields || []) | 
					
						
							|  |  |  |     .filter(field => field.startsWith(searchStr)) | 
					
						
							|  |  |  |     .map(field => field.replace(searchStr, '')); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const isAllowed = allowedFieldsHasKey || transformedFields.length > 0; | 
					
						
							|  |  |  |   const nextFields = allowedFieldsHasKey ? null : transformedFields; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return [nextFields, isAllowed]; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-24 02:40:10 -05:00
										 |  |  | const shouldRemoveAttribute = (model, key, attribute = {}, { withPrivate, isOutput }) => { | 
					
						
							| 
									
										
										
										
											2020-07-01 18:08:21 +02:00
										 |  |  |   const isPassword = attribute.type === 'password'; | 
					
						
							| 
									
										
										
										
											2020-10-01 17:47:08 +02:00
										 |  |  |   const isPrivate = isPrivateAttribute(model, key); | 
					
						
							| 
									
										
										
										
											2020-07-01 18:08:21 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |   const shouldRemovePassword = isOutput; | 
					
						
							|  |  |  |   const shouldRemovePrivate = !withPrivate && isOutput; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return !!((isPassword && shouldRemovePassword) || (isPrivate && shouldRemovePrivate)); | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-01 17:47:08 +02:00
										 |  |  | module.exports = sanitizeEntity; |