Merge branch 'develop' into fix/CW-7007

This commit is contained in:
Sivin Varghese
2026-05-08 19:54:38 +05:30
committed by GitHub
16 changed files with 39953 additions and 2575 deletions
+18 -10
View File
@@ -17,15 +17,12 @@ module Api::V1::InboxesHelper
def validate_imap(channel_data)
return unless channel_data.key?('imap_enabled') && channel_data[:imap_enabled]
Mail.defaults do
retriever_method :imap, { address: channel_data[:imap_address],
port: channel_data[:imap_port],
user_name: channel_data[:imap_login],
password: channel_data[:imap_password],
enable_ssl: channel_data[:imap_enable_ssl] }
end
# Validate the user-selected auth mechanism before opening the connection.
authentication = Imap::Authentication.validate_user_configurable!(channel_data[:imap_authentication])
check_imap_connection(channel_data)
# Use the same auth adapter as the fetch service so LOGIN uses the IMAP LOGIN command,
# not SASL AUTH=LOGIN.
check_imap_connection(channel_data, authentication)
end
def validate_smtp(channel_data)
@@ -37,8 +34,8 @@ module Api::V1::InboxesHelper
check_smtp_connection(channel_data, smtp)
end
def check_imap_connection(channel_data)
Mail.connection {} # rubocop:disable:block
def check_imap_connection(channel_data, authentication)
imap = open_imap_connection(channel_data, authentication)
rescue SocketError => e
raise StandardError, I18n.t('errors.inboxes.imap.socket_error')
rescue Net::IMAP::NoResponseError => e
@@ -53,9 +50,20 @@ module Api::V1::InboxesHelper
rescue StandardError => e
raise StandardError, e.message
ensure
imap.disconnect if imap.present? && !imap.disconnected?
Rails.logger.error "[Api::V1::InboxesHelper] check_imap_connection failed with #{e.message}" if e.present?
end
def open_imap_connection(channel_data, authentication)
imap = build_imap_connection(channel_data)
Imap::Authentication.authenticate!(imap, authentication, channel_data[:imap_login], channel_data[:imap_password])
imap
end
def build_imap_connection(channel_data)
Net::IMAP.new(channel_data[:imap_address], port: channel_data[:imap_port], ssl: channel_data[:imap_enable_ssl])
end
def check_smtp_connection(channel_data, smtp)
smtp.open_timeout = 10
smtp.start(channel_data[:smtp_domain], channel_data[:smtp_login], channel_data[:smtp_password],
@@ -1011,7 +1011,8 @@
"LABEL": "Password",
"PLACE_HOLDER": "Password"
},
"ENABLE_SSL": "Enable SSL"
"ENABLE_SSL": "Enable SSL",
"AUTH_MECHANISM": "Authentication"
},
"MICROSOFT": {
"TITLE": "Microsoft",
@@ -5,11 +5,13 @@ import SettingsFieldSection from 'dashboard/components-next/Settings/SettingsFie
import { useVuelidate } from '@vuelidate/core';
import { required, minLength } from '@vuelidate/validators';
import NextButton from 'dashboard/components-next/button/Button.vue';
import SingleSelectDropdown from './components/SingleSelectDropdown.vue';
export default {
components: {
SettingsFieldSection,
NextButton,
SingleSelectDropdown,
},
props: {
inbox: {
@@ -28,6 +30,12 @@ export default {
login: '',
password: '',
isSSLEnabled: true,
authMechanism: 'plain',
authMechanisms: [
{ key: 1, value: 'plain' },
{ key: 2, value: 'login' },
{ key: 3, value: 'cram-md5' },
],
};
},
validations: {
@@ -56,6 +64,7 @@ export default {
imap_login,
imap_password,
imap_enable_ssl,
imap_authentication,
} = this.inbox;
this.isIMAPEnabled = imap_enabled;
this.address = imap_address;
@@ -63,6 +72,7 @@ export default {
this.login = imap_login;
this.password = imap_password;
this.isSSLEnabled = imap_enable_ssl;
this.authMechanism = imap_authentication || 'plain';
},
async updateInbox() {
try {
@@ -77,6 +87,7 @@ export default {
imap_login: this.login,
imap_password: this.password,
imap_enable_ssl: this.isSSLEnabled,
imap_authentication: this.authMechanism,
},
};
@@ -90,6 +101,9 @@ export default {
useAlert(error.message);
}
},
handleAuthMechanismChange(mode) {
this.authMechanism = mode;
},
},
};
</script>
@@ -155,6 +169,13 @@ export default {
/>
{{ $t('INBOX_MGMT.IMAP.ENABLE_SSL') }}
</label>
<SingleSelectDropdown
class="w-full"
:label="$t('INBOX_MGMT.IMAP.AUTH_MECHANISM')"
:selected="authMechanism"
:options="authMechanisms"
:action="handleAuthMechanismChange"
/>
</div>
<NextButton
type="submit"
+2 -1
View File
@@ -6,6 +6,7 @@
# email :string not null
# forward_to_email :string not null
# imap_address :string default("")
# imap_authentication :string default("plain")
# imap_enable_ssl :boolean default(TRUE)
# imap_enabled :boolean default(FALSE)
# imap_login :string default("")
@@ -47,7 +48,7 @@ class Channel::Email < ApplicationRecord
end
self.table_name = 'channel_email'
EDITABLE_ATTRS = [:email, :imap_enabled, :imap_login, :imap_password, :imap_address, :imap_port, :imap_enable_ssl,
EDITABLE_ATTRS = [:email, :imap_enabled, :imap_login, :imap_password, :imap_address, :imap_port, :imap_enable_ssl, :imap_authentication,
:smtp_enabled, :smtp_login, :smtp_password, :smtp_address, :smtp_port, :smtp_domain, :smtp_enable_starttls_auto,
:smtp_enable_ssl_tls, :smtp_openssl_verify_mode, :smtp_authentication, :provider, :verified_for_sending].freeze
+31
View File
@@ -0,0 +1,31 @@
module Imap::Authentication
DEFAULT_MECHANISM = 'plain'.freeze
USER_CONFIGURABLE_MECHANISMS = %w[plain login cram-md5].freeze
module_function
def normalize(mechanism)
mechanism.presence || DEFAULT_MECHANISM
end
def validate_user_configurable!(mechanism)
normalized_mechanism = normalize(mechanism).to_s.downcase
return normalized_mechanism if USER_CONFIGURABLE_MECHANISMS.include?(normalized_mechanism)
allowed_values = USER_CONFIGURABLE_MECHANISMS.join(', ')
raise StandardError, "Invalid IMAP authentication mechanism. Allowed values: #{allowed_values}"
end
def authenticate!(imap, mechanism, username, password)
normalized_mechanism = normalize(mechanism).to_s.downcase
case normalized_mechanism
when 'cram-md5'
imap.authenticate('CRAM-MD5', username, password)
when 'login'
imap.login(username, password)
else
imap.authenticate(normalize(mechanism), username, password)
end
end
end
@@ -130,8 +130,9 @@ class Imap::BaseFetchEmailService
end
def build_imap_client
imap = Net::IMAP.new(channel.imap_address, port: channel.imap_port, ssl: true)
imap.authenticate(authentication_type, channel.imap_login, imap_password)
imap = Net::IMAP.new(channel.imap_address, port: channel.imap_port, ssl: channel.imap_enable_ssl)
Imap::Authentication.authenticate!(imap, authentication_type, channel.imap_login, imap_password)
imap.select('INBOX')
imap
end
+1 -1
View File
@@ -6,7 +6,7 @@ class Imap::FetchEmailService < Imap::BaseFetchEmailService
private
def authentication_type
'PLAIN'
channel.imap_authentication || 'plain'
end
def imap_password
@@ -90,6 +90,7 @@ if resource.email?
json.imap_port resource.channel.try(:imap_port)
json.imap_enabled resource.channel.try(:imap_enabled)
json.imap_enable_ssl resource.channel.try(:imap_enable_ssl)
json.imap_authentication resource.channel.try(:imap_authentication)
if resource.channel.try(:microsoft?) || resource.channel.try(:google?) || resource.channel.try(:legacy_google?)
json.reauthorization_required resource.channel.try(:provider_config).empty? || resource.channel.try(:reauthorization_required?)