diff --git a/social/email/61-email.html b/social/email/61-email.html
index 794919d6..304be8b5 100644
--- a/social/email/61-email.html
+++ b/social/email/61-email.html
@@ -443,9 +443,10 @@
@@ -478,6 +479,7 @@
keyFile: { value: "" },
mtausers: { value: [] },
auth: { value: false },
+ authType: { value: false },
expert: { value: '{"logger":false}' }
},
inputs: 0,
@@ -491,7 +493,8 @@
return this.name ? "node_label_italic" : "";
},
oneditprepare: function () {
- let node = this;
+ const node = this;
+
// Certificate settings
$("#node-input-secure").change(secVisibility);
$("#node-input-starttls").change(secVisibility);
@@ -505,9 +508,24 @@
}
}
// User Management
+ const noneType = { value: 'none', label: node._('email.label.noAuth'), hasValue: false }
+ const builtInType = { value: 'built-in', label: node._('email.label.auth'), hasValue: false }
+ const AUTH_TYPES = ['none', 'built-in', "flow", "global", "jsonata"]
+ if (!AUTH_TYPES.includes(node.authType)) {
+ // in-place upgrade
+ node.authType = (node.auth === "true" || node.auth === true) ? "built-in" : "none";
+ $('#node-input-authType').val(node.authType);
+ }
+ const $authTypedInput = $('#node-input-auth').typedInput({
+ default: 'none',
+ typeField: $('#node-input-authType'),
+ types: [noneType, builtInType, 'flow', 'global', 'jsonata']
+ })
+ $authTypedInput.change(builtInUsersVisibility);
+
let cacheItemCount = 0;
if (node.mtausers && node.mtausers.length > 0) {
- cacheItemCount = node.users.length;
+ cacheItemCount = node.mtausers.length;
node.mtausers.forEach(function (element, index, array) {
generateUserEntry(element, index);
});
@@ -561,7 +579,24 @@
$("#node-input-email-users-container").append(container);
}
-
+ function builtInUsersVisibility() {
+ const authType = $authTypedInput.typedInput('type');
+ if (authType === 'built-in') {
+ // show user list
+ $("#node-input-email-users-add").show();
+ $("#node-input-email-users-container-div").show();
+ $("#node-custom-auth-tip").hide();
+ } else {
+ $("#node-input-email-users-add").hide();
+ $("#node-input-email-users-container-div").hide();
+ if (authType === 'none') {
+ $("#node-custom-auth-tip").hide();
+ } else {
+ // show user tip for flow/global/jsonata
+ $("#node-custom-auth-tip").show();
+ }
+ }
+ }
$("#node-input-email-users-container").sortable({
axis: "y",
handle: ".node-input-email-users-handle",
@@ -579,20 +614,14 @@
$("#node-input-email-users-container-div").get(0).scrollHeight
);
});
- $("#node-input-auth").change(function () {
- if ($("#node-input-auth").is(":checked")) {
- $("#node-input-email-users-add").show();
- $("#node-input-email-users-container-div").show();
- } else {
- $("#node-input-email-users-add").hide();
- $("#node-input-email-users-container-div").hide();
- }
- });
+
// Expert settings
$("#node-input-expert").typedInput({
type: "json",
types: ["json"]
})
+
+ builtInUsersVisibility();
},
oneditsave: function () {
let node = this;
diff --git a/social/email/61-email.js b/social/email/61-email.js
index 90a52fa0..b423d0a8 100644
--- a/social/email/61-email.js
+++ b/social/email/61-email.js
@@ -631,6 +631,27 @@ module.exports = function(RED) {
this.keyFile = n.keyFile;
this.mtausers = n.mtausers;
this.auth = n.auth;
+ /** @type {"none"|"built-in"|"flow"|"global"|"jsonata"} **/
+ this.authType = n.authType;
+ const AUTH_TYPES = ['none', 'built-in', "flow", "global", "jsonata"]
+ if (!AUTH_TYPES.includes(this.authType)) {
+ // in-place upgrade
+ this.authType = (n.auth === "true" || n.auth === true) ? "built-in" : "none";
+ }
+
+ // basic user list duck type validation
+ const validateUserList = (users) => {
+ if (!Array.isArray(users) || users.length === 0) {
+ return false;
+ }
+ for (const user of users) {
+ if (!user.name || typeof user.password !== "string") {
+ return false;
+ }
+ }
+ return true;
+ }
+
try {
this.options = JSON.parse(n.expert);
} catch (error) {
@@ -650,7 +671,7 @@ module.exports = function(RED) {
if (!node.starttls) {
node.options.disabledCommands.push("STARTTLS");
}
- if (!node.auth) {
+ if (node.authType === "none") {
node.options.disabledCommands.push("AUTH");
}
@@ -686,14 +707,34 @@ module.exports = function(RED) {
});
}
- node.options.onAuth = function (auth, session, callback) {
- let id = node.mtausers.findIndex(function (item) {
- return item.name === auth.username;
- });
- if (id >= 0 && node.mtausers[id].password === auth.password) {
- callback(null, { user: id + 1 });
- } else {
- callback(new Error("Invalid username or password"));
+ node.options.onAuth = async function (auth, session, callback) {
+ try {
+ if (node.authType === "none") {
+ return; // auth not enabled - should not reach here
+ }
+ let userList;
+ if (node.authType === "built-in") {
+ // users defined in UI
+ userList = node.mtausers;
+ } else {
+ // users defined in flow, global, or jsonata
+ userList = await asyncEvaluateNodeProperty(RED, node.auth, node.authType, node, {});
+ }
+
+ if (!validateUserList(userList)) {
+ callback(new Error("Invalid user list"));
+ }
+
+ let id = userList.findIndex(function (item) {
+ return item.name === auth.username;
+ });
+ if (id >= 0 && userList[id].password === auth.password) {
+ callback(null, { user: id + 1 });
+ } else {
+ callback(new Error("Invalid username or password"));
+ }
+ } catch (error) {
+ callback(new Error("Invalid user list"));
}
}
@@ -711,4 +752,16 @@ module.exports = function(RED) {
}
RED.nodes.registerType("e-mail mta",EmailMtaNode);
+ function asyncEvaluateNodeProperty (RED, value, type, node, msg) {
+ return new Promise(function (resolve, reject) {
+ RED.util.evaluateNodeProperty(value, type, node, msg, function (e, r) {
+ if (e) {
+ reject(e)
+ } else {
+ resolve(r)
+ }
+ })
+ })
+ }
+
};