@growy/strapi-plugin-encrypted-field 1.8.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -8,7 +8,6 @@
8
8
 
9
9
  Plugin oficial de **Growy AI** para Strapi que proporciona un campo personalizado de texto cifrado con AES-256-GCM. Protege información sensible directamente en tu base de datos con cifrado transparente y validación robusta.
10
10
 
11
- ## Características
12
11
 
13
12
  - ✅ **Campo personalizado** "Texto Cifrado" en el Content-Type Builder
14
13
  - ✅ **Cifrado automático** AES-256-GCM al guardar
@@ -18,6 +17,7 @@ Plugin oficial de **Growy AI** para Strapi que proporciona un campo personalizad
18
17
  - ✅ **Gestión de claves robusta** con validación y mensajes de error claros
19
18
  - ✅ **Datos cifrados** en base de datos con IV único y Auth Tag
20
19
  - ✅ **Reutilizable** en cualquier colección o componente
20
+ - ✅ **Soporte completo** para componentes anidados y estructuras complejas
21
21
 
22
22
  ## Instalación
23
23
 
@@ -137,6 +137,7 @@ El campo funciona como un campo de texto normal:
137
137
  - **Al guardar**: Se cifra automáticamente
138
138
  - **Al leer**: Se descifra automáticamente
139
139
  - **En la BD**: Se guarda cifrado con formato `iv:authTag:encrypted`
140
+ - **En componentes**: Funciona igual en componentes anidados de cualquier profundidad
140
141
 
141
142
  ### 3. Uso por API
142
143
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@growy/strapi-plugin-encrypted-field",
3
- "version": "1.8.1",
3
+ "version": "2.0.0",
4
4
  "description": "Campo personalizado de texto cifrado para Strapi",
5
5
  "strapi": {
6
6
  "name": "encrypted-field",
@@ -7,29 +7,16 @@ module.exports = ({ strapi }) => {
7
7
  const components = Object.values(strapi.components);
8
8
  const allModels = [...contentTypes, ...components];
9
9
 
10
- strapi.log.info(`Total de content types: ${contentTypes.length}, componentes: ${components.length}`);
11
-
12
10
  allModels.forEach((model) => {
13
11
  const attributes = model.attributes || {};
14
12
 
15
- // Debug: mostrar todos los campos del modelo
16
- strapi.log.debug(`Modelo ${model.uid}: ${Object.keys(attributes).length} atributos`);
17
-
18
13
  const encryptedFields = Object.entries(attributes)
19
- .filter(([key, attr]) => {
20
- const isEncrypted = isEncryptedField(attr);
21
- if (attr.customField) {
22
- strapi.log.debug(` - ${key}: customField = ${attr.customField}, isEncrypted = ${isEncrypted}`);
23
- }
24
- return isEncrypted;
25
- })
14
+ .filter(([key, attr]) => isEncryptedField(attr))
26
15
  .map(([key]) => key);
27
16
 
28
17
  if (encryptedFields.length === 0) return;
29
18
 
30
19
  const uid = model.uid;
31
-
32
- strapi.log.info(`✓ Registrando lifecycles de cifrado para ${uid} - Campos: ${encryptedFields.join(', ')}`);
33
20
 
34
21
 
35
22
  strapi.db.lifecycles.subscribe({
@@ -38,9 +25,6 @@ module.exports = ({ strapi }) => {
38
25
  async beforeCreate(event) {
39
26
  const { data } = event.params;
40
27
 
41
- strapi.log.info(`[beforeCreate] Evento recibido para ${event.model?.uid}`);
42
- strapi.log.info(`[beforeCreate] Data recibida: ${JSON.stringify(data)}`);
43
-
44
28
  if (!event.model?.uid) return;
45
29
 
46
30
  const currentModel = strapi.getModel(event.model.uid);
@@ -50,26 +34,17 @@ module.exports = ({ strapi }) => {
50
34
  for (const [key, attribute] of Object.entries(currentModel.attributes)) {
51
35
  if (!isEncryptedField(attribute)) continue;
52
36
 
53
- strapi.log.info(`[beforeCreate] Campo cifrado detectado: ${key}`);
54
-
55
37
  if (Object.prototype.hasOwnProperty.call(data, key)) {
56
38
  const value = data[key];
57
39
 
58
- strapi.log.info(`[beforeCreate] Valor del campo ${key}: ${value}`);
59
-
60
- if (value === null || value === undefined || value === '') {
61
- strapi.log.info(`[beforeCreate] Valor vacío, saltando cifrado`);
62
- continue;
63
- }
40
+ if (value === null || value === undefined || value === '') continue;
64
41
 
65
42
  const validation = validateValue(value, attribute);
66
43
  if (!validation.valid) {
67
44
  throw new Error(`Validación fallida para el campo "${key}": ${validation.error}`);
68
45
  }
69
46
 
70
- strapi.log.info(`✓ Cifrando campo ${key} con valor: ${value}`);
71
47
  data[key] = encrypt(value, strapi);
72
- strapi.log.info(`✓ Campo ${key} cifrado exitosamente`);
73
48
  }
74
49
  }
75
50
  },
@@ -93,7 +68,6 @@ module.exports = ({ strapi }) => {
93
68
  throw new Error(`Validación fallida para el campo "${key}": ${validation.error}`);
94
69
  }
95
70
 
96
- strapi.log.info(`Cifrando campo ${key} en ${event.model.uid}`);
97
71
  data[key] = encrypt(value, strapi);
98
72
  }
99
73
  }
@@ -113,9 +87,7 @@ module.exports = ({ strapi }) => {
113
87
  if (Object.prototype.hasOwnProperty.call(result, key)) {
114
88
  const value = result[key];
115
89
  if (typeof value === 'string' && value) {
116
- const decrypted = decrypt(value, strapi);
117
- strapi.log.info(`Descifrando campo ${key} en ${event.model.uid}: ${value.substring(0, 20)}... -> ${decrypted}`);
118
- result[key] = decrypted;
90
+ result[key] = decrypt(value, strapi);
119
91
  }
120
92
  }
121
93
  }
@@ -136,9 +108,7 @@ module.exports = ({ strapi }) => {
136
108
  if (Object.prototype.hasOwnProperty.call(item, key)) {
137
109
  const value = item[key];
138
110
  if (typeof value === 'string' && value) {
139
- const decrypted = decrypt(value, strapi);
140
- strapi.log.info(`Descifrando campo ${key}: ${value.substring(0, 20)}... -> ${decrypted}`);
141
- item[key] = decrypted;
111
+ item[key] = decrypt(value, strapi);
142
112
  }
143
113
  }
144
114
  }
@@ -4,40 +4,61 @@ module.exports = (config, { strapi }) => {
4
4
  return async (ctx, next) => {
5
5
  await next();
6
6
 
7
- if (!ctx.body || !ctx.body.data) return;
7
+ if (!ctx.body) return;
8
8
 
9
- const decryptData = (data) => {
10
- if (!data || typeof data !== 'object') return;
9
+ // Función recursiva para descifrar campos en cualquier nivel de anidación
10
+ const decryptRecursive = (obj, modelUid = null) => {
11
+ if (!obj || typeof obj !== 'object') return;
11
12
 
12
- const contentType = ctx.params?.model || ctx.state?.route?.info?.type;
13
- if (!contentType) return;
14
-
15
- let model;
16
- try {
17
- model = strapi.getModel(contentType);
18
- } catch (e) {
13
+ // Si es un array, procesar cada elemento
14
+ if (Array.isArray(obj)) {
15
+ obj.forEach(item => decryptRecursive(item, modelUid));
19
16
  return;
20
17
  }
21
18
 
22
- if (!model?.attributes) return;
19
+ // Detectar si es un componente por el campo __component
20
+ let currentModelUid = modelUid;
21
+ if (obj.__component) {
22
+ currentModelUid = obj.__component;
23
+ }
24
+
25
+ // Obtener el modelo si tenemos un UID
26
+ let model = null;
27
+ if (currentModelUid) {
28
+ try {
29
+ model = strapi.getModel(currentModelUid) || strapi.components[currentModelUid];
30
+ } catch (e) {
31
+ // Ignorar si no se encuentra el modelo
32
+ }
33
+ }
23
34
 
24
- for (const [key, attribute] of Object.entries(model.attributes)) {
25
- if (!isEncryptedField(attribute)) continue;
26
-
27
- if (data[key] && typeof data[key] === 'string') {
28
- try {
29
- data[key] = decrypt(data[key], strapi);
30
- } catch (error) {
31
- strapi.log.error(`Error descifrando campo ${key}: ${error.message}`);
35
+ // Descifrar campos del modelo actual
36
+ if (model?.attributes) {
37
+ for (const [key, attribute] of Object.entries(model.attributes)) {
38
+ if (isEncryptedField(attribute) && obj[key] && typeof obj[key] === 'string') {
39
+ try {
40
+ obj[key] = decrypt(obj[key], strapi);
41
+ } catch (error) {
42
+ strapi.log.error(`Error descifrando campo ${key}: ${error.message}`);
43
+ }
32
44
  }
33
45
  }
34
46
  }
47
+
48
+ // Procesar recursivamente todos los valores del objeto
49
+ for (const value of Object.values(obj)) {
50
+ if (value && typeof value === 'object') {
51
+ decryptRecursive(value, currentModelUid);
52
+ }
53
+ }
35
54
  };
36
55
 
37
- if (Array.isArray(ctx.body.data)) {
38
- ctx.body.data.forEach(decryptData);
56
+ // Procesar ctx.body.data si existe
57
+ if (ctx.body.data) {
58
+ decryptRecursive(ctx.body.data);
39
59
  } else {
40
- decryptData(ctx.body.data);
60
+ // Procesar ctx.body directamente
61
+ decryptRecursive(ctx.body);
41
62
  }
42
63
  };
43
64
  };
@@ -1,4 +1,8 @@
1
1
  module.exports = ({ strapi }) => {
2
+ const decryptMiddleware = require('./middlewares/decrypt');
3
+
4
+ strapi.server.use(decryptMiddleware({}, { strapi }));
5
+
2
6
  strapi.customFields.register({
3
7
  name: 'encrypted-text',
4
8
  plugin: 'encrypted-field',