mirror of
https://github.com/wekan/wekan.git
synced 2025-04-21 12:37:07 -04:00
536 lines
15 KiB
JavaScript
536 lines
15 KiB
JavaScript
import { ReactiveCache } from '/imports/reactiveCache';
|
|
import { TAPi18n } from '/imports/i18n';
|
|
//var nodemailer = require('nodemailer');
|
|
|
|
// Sandstorm context is detected using the METEOR_SETTINGS environment variable
|
|
// in the package definition.
|
|
const isSandstorm =
|
|
Meteor.settings && Meteor.settings.public && Meteor.settings.public.sandstorm;
|
|
|
|
Settings = new Mongo.Collection('settings');
|
|
|
|
Settings.attachSchema(
|
|
new SimpleSchema({
|
|
disableRegistration: {
|
|
type: Boolean,
|
|
optional: true,
|
|
defaultValue: false,
|
|
},
|
|
disableForgotPassword: {
|
|
type: Boolean,
|
|
optional: true,
|
|
defaultValue: false,
|
|
},
|
|
'mailServer.username': {
|
|
type: String,
|
|
optional: true,
|
|
},
|
|
'mailServer.password': {
|
|
type: String,
|
|
optional: true,
|
|
},
|
|
'mailServer.host': {
|
|
type: String,
|
|
optional: true,
|
|
},
|
|
'mailServer.port': {
|
|
type: String,
|
|
optional: true,
|
|
},
|
|
'mailServer.enableTLS': {
|
|
type: Boolean,
|
|
optional: true,
|
|
},
|
|
'mailServer.from': {
|
|
type: String,
|
|
optional: true,
|
|
},
|
|
productName: {
|
|
type: String,
|
|
optional: true,
|
|
},
|
|
displayAuthenticationMethod: {
|
|
type: Boolean,
|
|
optional: true,
|
|
},
|
|
defaultAuthenticationMethod: {
|
|
type: String,
|
|
optional: false,
|
|
},
|
|
spinnerName: {
|
|
type: String,
|
|
optional: true,
|
|
},
|
|
hideLogo: {
|
|
type: Boolean,
|
|
optional: true,
|
|
},
|
|
hideCardCounterList: {
|
|
type: Boolean,
|
|
optional: true,
|
|
},
|
|
hideBoardMemberList: {
|
|
type: Boolean,
|
|
optional: true,
|
|
},
|
|
customLoginLogoImageUrl: {
|
|
type: String,
|
|
optional: true,
|
|
},
|
|
customLoginLogoLinkUrl: {
|
|
type: String,
|
|
optional: true,
|
|
},
|
|
customHelpLinkUrl: {
|
|
type: String,
|
|
optional: true,
|
|
},
|
|
textBelowCustomLoginLogo: {
|
|
type: String,
|
|
optional: true,
|
|
},
|
|
automaticLinkedUrlSchemes: {
|
|
type: String,
|
|
optional: true,
|
|
},
|
|
customTopLeftCornerLogoImageUrl: {
|
|
type: String,
|
|
optional: true,
|
|
},
|
|
customTopLeftCornerLogoLinkUrl: {
|
|
type: String,
|
|
optional: true,
|
|
},
|
|
customTopLeftCornerLogoHeight: {
|
|
type: String,
|
|
optional: true,
|
|
},
|
|
oidcBtnText: {
|
|
type: String,
|
|
optional: true,
|
|
},
|
|
mailDomainName: {
|
|
type: String,
|
|
optional: true,
|
|
},
|
|
legalNotice: {
|
|
type: String,
|
|
optional: true,
|
|
},
|
|
createdAt: {
|
|
type: Date,
|
|
denyUpdate: true,
|
|
// eslint-disable-next-line consistent-return
|
|
autoValue() {
|
|
if (this.isInsert) {
|
|
return new Date();
|
|
} else if (this.isUpsert) {
|
|
return { $setOnInsert: new Date() };
|
|
} else {
|
|
this.unset();
|
|
}
|
|
},
|
|
},
|
|
modifiedAt: {
|
|
type: Date,
|
|
// eslint-disable-next-line consistent-return
|
|
autoValue() {
|
|
if (this.isInsert || this.isUpsert || this.isUpdate) {
|
|
return new Date();
|
|
} else {
|
|
this.unset();
|
|
}
|
|
},
|
|
},
|
|
}),
|
|
);
|
|
Settings.helpers({
|
|
mailUrl() {
|
|
if (!this.mailServer.host) {
|
|
return null;
|
|
}
|
|
const protocol = this.mailServer.enableTLS ? 'smtps://' : 'smtp://';
|
|
if (!this.mailServer.username && !this.mailServer.password) {
|
|
return `${protocol}${this.mailServer.host}:${this.mailServer.port}/`;
|
|
}
|
|
return `${protocol}${this.mailServer.username}:${encodeURIComponent(
|
|
this.mailServer.password,
|
|
)}@${this.mailServer.host}:${this.mailServer.port}/`;
|
|
},
|
|
});
|
|
Settings.allow({
|
|
update(userId) {
|
|
const user = ReactiveCache.getUser(userId);
|
|
return user && user.isAdmin;
|
|
},
|
|
});
|
|
|
|
if (Meteor.isServer) {
|
|
Meteor.startup(() => {
|
|
Settings._collection.createIndex({ modifiedAt: -1 });
|
|
const setting = ReactiveCache.getCurrentSetting();
|
|
if (!setting) {
|
|
const now = new Date();
|
|
const domain = process.env.ROOT_URL.match(
|
|
/\/\/(?:www\.)?(.*)?(?:\/)?/,
|
|
)[1];
|
|
const from = `Boards Support <support@${domain}>`;
|
|
const defaultSetting = {
|
|
disableRegistration: false,
|
|
mailServer: {
|
|
username: '',
|
|
password: '',
|
|
host: '',
|
|
port: '',
|
|
enableTLS: false,
|
|
from,
|
|
},
|
|
createdAt: now,
|
|
modifiedAt: now,
|
|
displayAuthenticationMethod: true,
|
|
defaultAuthenticationMethod: 'password',
|
|
};
|
|
Settings.insert(defaultSetting);
|
|
}
|
|
if (isSandstorm) {
|
|
// At Sandstorm, Admin Panel has SMTP settings
|
|
const newSetting = ReactiveCache.getCurrentSetting();
|
|
if (!process.env.MAIL_URL && newSetting.mailUrl())
|
|
process.env.MAIL_URL = newSetting.mailUrl();
|
|
Accounts.emailTemplates.from = process.env.MAIL_FROM
|
|
? process.env.MAIL_FROM
|
|
: newSetting.mailServer.from;
|
|
} else {
|
|
// Not running on Sandstorm, so using environment variables
|
|
Accounts.emailTemplates.from = process.env.MAIL_FROM;
|
|
}
|
|
});
|
|
if (isSandstorm) {
|
|
// At Sandstorm Wekan Admin Panel, save SMTP settings.
|
|
Settings.after.update((userId, doc, fieldNames) => {
|
|
// assign new values to mail-from & MAIL_URL in environment
|
|
if (_.contains(fieldNames, 'mailServer') && doc.mailServer.host) {
|
|
const protocol = doc.mailServer.enableTLS ? 'smtps://' : 'smtp://';
|
|
if (!doc.mailServer.username && !doc.mailServer.password) {
|
|
process.env.MAIL_URL = `${protocol}${doc.mailServer.host}:${doc.mailServer.port}/`;
|
|
} else {
|
|
process.env.MAIL_URL = `${protocol}${
|
|
doc.mailServer.username
|
|
}:${encodeURIComponent(doc.mailServer.password)}@${
|
|
doc.mailServer.host
|
|
}:${doc.mailServer.port}/`;
|
|
}
|
|
Accounts.emailTemplates.from = doc.mailServer.from;
|
|
}
|
|
});
|
|
}
|
|
|
|
function getRandomNum(min, max) {
|
|
const range = max - min;
|
|
const rand = Math.random();
|
|
return min + Math.round(rand * range);
|
|
}
|
|
|
|
function getEnvVar(name) {
|
|
const value = process.env[name];
|
|
if (value) {
|
|
return value;
|
|
}
|
|
throw new Meteor.Error([
|
|
'var-not-exist',
|
|
`The environment variable ${name} does not exist`,
|
|
]);
|
|
}
|
|
|
|
function loadOidcConfig(service){
|
|
check(service, String);
|
|
var config = ServiceConfiguration.configurations.findOne({service: service});
|
|
return config;
|
|
}
|
|
|
|
function sendInvitationEmail(_id) {
|
|
const icode = ReactiveCache.getInvitationCode(_id);
|
|
const author = ReactiveCache.getCurrentUser();
|
|
try {
|
|
const fullName = ReactiveCache.getUser(icode.authorId)?.profile?.fullname || "";
|
|
|
|
const params = {
|
|
email: icode.email,
|
|
inviter: fullName != "" ? fullName + " (" + ReactiveCache.getUser(icode.authorId).username + " )" : ReactiveCache.getUser(icode.authorId).username,
|
|
user: icode.email.split('@')[0],
|
|
icode: icode.code,
|
|
url: FlowRouter.url('sign-up'),
|
|
};
|
|
const lang = author.getLanguage();
|
|
/*
|
|
if (process.env.MAIL_SERVICE !== '') {
|
|
let transporter = nodemailer.createTransport({
|
|
service: process.env.MAIL_SERVICE,
|
|
auth: {
|
|
user: process.env.MAIL_SERVICE_USER,
|
|
pass: process.env.MAIL_SERVICE_PASSWORD
|
|
},
|
|
})
|
|
let info = transporter.sendMail({
|
|
to: icode.email,
|
|
from: Accounts.emailTemplates.from,
|
|
subject: TAPi18n.__('email-invite-register-subject', params, lang),
|
|
text: TAPi18n.__('email-invite-register-text', params, lang),
|
|
})
|
|
} else {
|
|
Email.send({
|
|
to: icode.email,
|
|
from: Accounts.emailTemplates.from,
|
|
subject: TAPi18n.__('email-invite-register-subject', params, lang),
|
|
text: TAPi18n.__('email-invite-register-text', params, lang),
|
|
});
|
|
}
|
|
*/
|
|
Email.send({
|
|
to: icode.email,
|
|
from: Accounts.emailTemplates.from,
|
|
subject: TAPi18n.__('email-invite-register-subject', params, lang),
|
|
text: TAPi18n.__('email-invite-register-text', params, lang),
|
|
});
|
|
} catch (e) {
|
|
InvitationCodes.remove(_id);
|
|
throw new Meteor.Error('email-fail', e.message);
|
|
}
|
|
}
|
|
|
|
function isNonAdminAllowedToSendMail(currentUser){
|
|
const currSett = ReactiveCache.getCurrentSetting();
|
|
let isAllowed = false;
|
|
if(currSett && currSett != undefined && currSett.disableRegistration && currSett.mailDomainName !== undefined && currSett.mailDomainName != ""){
|
|
for(let i = 0; i < currentUser.emails.length; i++) {
|
|
if(currentUser.emails[i].address.endsWith(currSett.mailDomainName)){
|
|
isAllowed = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return isAllowed;
|
|
}
|
|
|
|
function isLdapEnabled() {
|
|
return (
|
|
process.env.LDAP_ENABLE === 'true' || process.env.LDAP_ENABLE === true
|
|
);
|
|
}
|
|
|
|
function isOauth2Enabled() {
|
|
return (
|
|
process.env.OAUTH2_ENABLED === 'true' ||
|
|
process.env.OAUTH2_ENABLED === true
|
|
);
|
|
}
|
|
|
|
function isCasEnabled() {
|
|
return (
|
|
process.env.CAS_ENABLED === 'true' || process.env.CAS_ENABLED === true
|
|
);
|
|
}
|
|
|
|
function isApiEnabled() {
|
|
return process.env.WITH_API === 'true' || process.env.WITH_API === true;
|
|
}
|
|
|
|
Meteor.methods({
|
|
sendInvitation(emails, boards) {
|
|
let rc = 0;
|
|
check(emails, [String]);
|
|
check(boards, [String]);
|
|
|
|
const user = ReactiveCache.getCurrentUser();
|
|
if (!user.isAdmin && !isNonAdminAllowedToSendMail(user)) {
|
|
rc = -1;
|
|
throw new Meteor.Error('not-allowed');
|
|
}
|
|
emails.forEach(email => {
|
|
if (email && SimpleSchema.RegEx.Email.test(email)) {
|
|
// Checks if the email is already link to an account.
|
|
const userExist = ReactiveCache.getUser({ email });
|
|
if (userExist) {
|
|
rc = -1;
|
|
throw new Meteor.Error(
|
|
'user-exist',
|
|
`The user with the email ${email} has already an account.`,
|
|
);
|
|
}
|
|
// Checks if the email is already link to an invitation.
|
|
const invitation = ReactiveCache.getInvitationCode({ email });
|
|
if (invitation) {
|
|
InvitationCodes.update(invitation, {
|
|
$set: { boardsToBeInvited: boards },
|
|
});
|
|
sendInvitationEmail(invitation._id);
|
|
} else {
|
|
const code = getRandomNum(100000, 999999);
|
|
InvitationCodes.insert(
|
|
{
|
|
code,
|
|
email,
|
|
boardsToBeInvited: boards,
|
|
createdAt: new Date(),
|
|
authorId: Meteor.userId(),
|
|
},
|
|
function(err, _id) {
|
|
if (!err && _id) {
|
|
sendInvitationEmail(_id);
|
|
} else {
|
|
rc = -1;
|
|
throw new Meteor.Error(
|
|
'invitation-generated-fail',
|
|
err.message,
|
|
);
|
|
}
|
|
},
|
|
);
|
|
}
|
|
}
|
|
});
|
|
return rc;
|
|
},
|
|
|
|
sendSMTPTestEmail() {
|
|
if (!Meteor.userId()) {
|
|
throw new Meteor.Error('invalid-user');
|
|
}
|
|
const user = ReactiveCache.getCurrentUser();
|
|
if (!user.emails || !user.emails[0] || !user.emails[0].address) {
|
|
throw new Meteor.Error('email-invalid');
|
|
}
|
|
this.unblock();
|
|
const lang = user.getLanguage();
|
|
try {
|
|
/*
|
|
if (process.env.MAIL_SERVICE !== '') {
|
|
let transporter = nodemailer.createTransport({
|
|
service: process.env.MAIL_SERVICE,
|
|
auth: {
|
|
user: process.env.MAIL_SERVICE_USER,
|
|
pass: process.env.MAIL_SERVICE_PASSWORD
|
|
},
|
|
})
|
|
let info = transporter.sendMail({
|
|
to: user.emails[0].address,
|
|
from: Accounts.emailTemplates.from,
|
|
subject: TAPi18n.__('email-smtp-test-subject', { lng: lang }),
|
|
text: TAPi18n.__('email-smtp-test-text', { lng: lang }),
|
|
})
|
|
} else {
|
|
Email.send({
|
|
to: user.emails[0].address,
|
|
from: Accounts.emailTemplates.from,
|
|
subject: TAPi18n.__('email-smtp-test-subject', { lng: lang }),
|
|
text: TAPi18n.__('email-smtp-test-text', { lng: lang }),
|
|
});
|
|
}
|
|
*/
|
|
Email.send({
|
|
to: user.emails[0].address,
|
|
from: Accounts.emailTemplates.from,
|
|
subject: TAPi18n.__('email-smtp-test-subject', { lng: lang }),
|
|
text: TAPi18n.__('email-smtp-test-text', { lng: lang }),
|
|
});
|
|
} catch ({ message }) {
|
|
throw new Meteor.Error(
|
|
'email-fail',
|
|
`${TAPi18n.__('email-fail-text', { lng: lang })}: ${message}`,
|
|
message,
|
|
);
|
|
}
|
|
return {
|
|
message: 'email-sent',
|
|
email: user.emails[0].address,
|
|
};
|
|
},
|
|
|
|
getCustomUI() {
|
|
const setting = ReactiveCache.getCurrentSetting();
|
|
if (!setting.productName) {
|
|
return {
|
|
productName: '',
|
|
};
|
|
} else {
|
|
return {
|
|
productName: `${setting.productName}`,
|
|
};
|
|
}
|
|
},
|
|
|
|
isDisableRegistration() {
|
|
const setting = ReactiveCache.getCurrentSetting();
|
|
if (setting.disableRegistration === true) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
},
|
|
|
|
isDisableForgotPassword() {
|
|
const setting = ReactiveCache.getCurrentSetting();
|
|
if (setting.disableForgotPassword === true) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
},
|
|
|
|
getMatomoConf() {
|
|
return {
|
|
address: getEnvVar('MATOMO_ADDRESS'),
|
|
siteId: getEnvVar('MATOMO_SITE_ID'),
|
|
doNotTrack: process.env.MATOMO_DO_NOT_TRACK || false,
|
|
withUserName: process.env.MATOMO_WITH_USERNAME || false,
|
|
};
|
|
},
|
|
|
|
_isLdapEnabled() {
|
|
return isLdapEnabled();
|
|
},
|
|
|
|
_isOauth2Enabled() {
|
|
return isOauth2Enabled();
|
|
},
|
|
|
|
_isCasEnabled() {
|
|
return isCasEnabled();
|
|
},
|
|
|
|
_isApiEnabled() {
|
|
return isApiEnabled();
|
|
},
|
|
|
|
// Gets all connection methods to use it in the Template
|
|
getAuthenticationsEnabled() {
|
|
return {
|
|
ldap: isLdapEnabled(),
|
|
oauth2: isOauth2Enabled(),
|
|
cas: isCasEnabled(),
|
|
};
|
|
},
|
|
|
|
getOauthServerUrl(){
|
|
return process.env.OAUTH2_SERVER_URL;
|
|
},
|
|
getOauthDashboardUrl(){
|
|
return process.env.DASHBOARD_URL;
|
|
},
|
|
getDefaultAuthenticationMethod() {
|
|
return process.env.DEFAULT_AUTHENTICATION_METHOD;
|
|
},
|
|
|
|
isPasswordLoginEnabled() {
|
|
return !(process.env.PASSWORD_LOGIN_ENABLED === 'false');
|
|
},
|
|
isOidcRedirectionEnabled(){
|
|
return process.env.OIDC_REDIRECTION_ENABLED === 'true' && Object.keys(loadOidcConfig("oidc")).length > 0;
|
|
},
|
|
getServiceConfiguration(service){
|
|
return loadOidcConfig(service);
|
|
}
|
|
});
|
|
}
|
|
|
|
export default Settings;
|