-
-
Notifications
You must be signed in to change notification settings - Fork 9.2k
Closed
Labels
issue: feature requestIssue suggesting a new featureIssue suggesting a new feature
Description
Bug report
Required System information
- Node.js version: v20.0.9
- NPM version: 10.1.0
- Strapi version: 4.19.1
- Database: sqlite
- Operating system: Macos
- Is your project Javascript or Typescript: Javascript
Describe the bug
When using the plugin users-permissions to update one the provided email templates we cannot use custom fields from the user schema.
Indeed we can see that the authorized keys only contain USER.email and USER.username.
Steps to reproduce the behavior
- Go to
Settings - Go to
Email templates - Click on one of the email templates (Reset password or Email address confirmation)
- Fill the
Messagefield with<%= USER.firstName %>(or any other configured field in the User content type) - See error
Invalid template
Expected behavior
Actually I guess this is the expected behavior but we may want to be able to use any field from user schema.
Mitigating the issue
We can override this behavior to be able to use any field from user schema.
Create a file ./src/extensions/users-permissions/overrides.js which is a copy of the original function where we use our custom email validation
'use strict';
const _ = require("lodash");
// Addition : use our custom validation
const { isValidEmailTemplate } = require("./email-template");
module.exports = {
async updateEmailTemplate(ctx) {
if (_.isEmpty(ctx.request.body)) {
throw new ValidationError('Request body cannot be empty');
}
const emailTemplates = ctx.request.body['email-templates'];
for (const key of Object.keys(emailTemplates)) {
const template = emailTemplates[key].options.message;
if (!isValidEmailTemplate(template)) {
throw new ValidationError('Invalid template');
}
}
await strapi
.store({type: 'plugin', name: 'users-permissions', key: 'email'})
.set({value: emailTemplates});
ctx.send({ok: true});
}
}Create a file ./src/extensions/users-permissions/email-template.js which is a copy of the original rule where we change the authorizedKeys array
'use strict';
const { trim } = require('lodash/fp');
const {
template: { createLooseInterpolationRegExp, createStrictInterpolationRegExp },
} = require('@strapi/utils');
const invalidPatternsRegexes = [
// Ignore "evaluation" patterns: <% ... %>
/<%[^=]([\s\S]*?)%>/m,
// Ignore basic string interpolations
/\${([^{}]*)}/m,
];
// Addition : get the user schema
const userSchema = strapi.getModel('plugin::users-permissions.user');
const authorizedKeys = [
'URL',
'ADMIN_URL',
'SERVER_URL',
'CODE',
'USER',
// Addition : spread user attributes
...Object.entries(userSchema.attributes).map(([key, value]) => `USER.${key}`),
'TOKEN',
];
const matchAll = (pattern, src) => {
const matches = [];
let match;
const regexPatternWithGlobal = RegExp(pattern, 'g');
// eslint-disable-next-line no-cond-assign
while ((match = regexPatternWithGlobal.exec(src))) {
const [, group] = match;
matches.push(trim(group));
}
return matches;
};
const isValidEmailTemplate = (template) => {
// Check for known invalid patterns
for (const reg of invalidPatternsRegexes) {
if (reg.test(template)) {
return false;
}
}
const interpolation = {
// Strict interpolation pattern to match only valid groups
strict: createStrictInterpolationRegExp(authorizedKeys),
// Weak interpolation pattern to match as many group as possible.
loose: createLooseInterpolationRegExp(),
};
// Compute both strict & loose matches
const strictMatches = matchAll(interpolation.strict, template);
const looseMatches = matchAll(interpolation.loose, template);
// If we have more matches with the loose RegExp than with the strict one,
// then it means that at least one of the interpolation group is invalid
// Note: In the future, if we wanted to give more details for error formatting
// purposes, we could return the difference between the two arrays
if (looseMatches.length > strictMatches.length) {
return false;
}
return true;
};
module.exports = {
isValidEmailTemplate,
};Now we can override the controller function in ./src/index.js
'use strict';
module.exports = {
/**
* An asynchronous register function that runs before
* your application is initialized.
*
* This gives you an opportunity to extend code.
*/
register({ strapi }) {
strapi.controllers['plugin::users-permissions.settings'].updateEmailTemplate = require('./extensions/users-permissions/overrides').updateEmailTemplate;
},
/**
* An asynchronous bootstrap function that runs before
* your application gets started.
*
* This gives you an opportunity to set up your data model,
* run jobs, or perform some special logic.
*/
bootstrap(/*{ strapi }*/) {},
};Metadata
Metadata
Assignees
Labels
issue: feature requestIssue suggesting a new featureIssue suggesting a new feature