diff --git a/app/controllers/api/v1/accounts/conversations_controller.rb b/app/controllers/api/v1/accounts/conversations_controller.rb index 22eee629e..0515eabca 100644 --- a/app/controllers/api/v1/accounts/conversations_controller.rb +++ b/app/controllers/api/v1/accounts/conversations_controller.rb @@ -61,6 +61,7 @@ class Api::V1::Accounts::ConversationsController < Api::V1::Accounts::BaseContro else @status = @conversation.toggle_status end + assign_conversation if @conversation.status == 'open' && Current.user.is_a?(User) && Current.user&.agent? end def toggle_typing_status @@ -93,6 +94,11 @@ class Api::V1::Accounts::ConversationsController < Api::V1::Accounts::BaseContro @conversation.snoozed_until = parse_date_time(params[:snoozed_until].to_s) if params[:snoozed_until] end + def assign_conversation + @agent = Current.account.users.find(current_user.id) + @conversation.update_assignee(@agent) + end + def trigger_typing_event(event, is_private) user = current_user.presence || @resource Rails.configuration.dispatcher.dispatch(event, Time.zone.now, conversation: @conversation, user: user, is_private: is_private) diff --git a/app/helpers/api/v1/inboxes_helper.rb b/app/helpers/api/v1/inboxes_helper.rb index 08f000cc6..8cdf8d987 100644 --- a/app/helpers/api/v1/inboxes_helper.rb +++ b/app/helpers/api/v1/inboxes_helper.rb @@ -26,8 +26,48 @@ module Api::V1::InboxesHelper def validate_smtp(channel_data) return unless channel_data.key?('smtp_enabled') && channel_data[:smtp_enabled] - smtp = Net::SMTP.start(channel_data[:smtp_address], channel_data[:smtp_port], channel_data[:smtp_domain], channel_data[:smtp_email], - channel_data[:smtp_password], :login) + smtp = Net::SMTP.new(channel_data[:smtp_address], channel_data[:smtp_port]) + + set_smtp_encryption(channel_data, smtp) + + smtp.start(channel_data[:smtp_domain], channel_data[:smtp_email], channel_data[:smtp_password], :login) smtp.finish unless smtp&.nil? end + + def set_smtp_encryption(channel_data, smtp) + if channel_data[:smtp_enable_ssl_tls] + set_enable_tls(channel_data, smtp) + elsif channel_data[:smtp_enable_starttls_auto] + set_enable_starttls_auto(channel_data, smtp) + end + end + + def set_enable_starttls_auto(channel_data, smtp) + return unless smtp.respond_to?(:enable_starttls_auto) + + if channel_data[:smtp_openssl_verify_mode] + context = enable_openssl_mode(channel_data[:smtp_openssl_verify_mode]) + smtp.enable_starttls_auto(context) + else + smtp.enable_starttls_auto + end + end + + def set_enable_tls(channel_data, smtp) + return unless smtp.respond_to?(:enable_tls) + + if channel_data[:smtp_openssl_verify_mode] + context = enable_openssl_mode(channel_data[:smtp_openssl_verify_mode]) + smtp.enable_tls(context) + else + smtp.enable_tls + end + end + + def enable_openssl_mode(smtp_openssl_verify_mode) + openssl_verify_mode = "OpenSSL::SSL::VERIFY_#{smtp_openssl_verify_mode.upcase}".constantize if smtp_openssl_verify_mode.is_a?(String) + context = Net::SMTP.default_ssl_context + context.verify_mode = openssl_verify_mode + context + end end diff --git a/app/javascript/dashboard/i18n/locale/en/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/en/inboxMgmt.json index 86cf1a3c3..27172cdba 100644 --- a/app/javascript/dashboard/i18n/locale/en/inboxMgmt.json +++ b/app/javascript/dashboard/i18n/locale/en/inboxMgmt.json @@ -522,7 +522,11 @@ "DOMAIN": { "LABEL": "Domain", "PLACE_HOLDER": "Domain" - } + }, + "ENCRYPTION": "Encryption", + "SSL_TLS": "SSL/TLS", + "START_TLS": "STARTTLS", + "OPEN_SSL_VERIFY_MODE": "Open SSL Verify Mode" } } } diff --git a/app/javascript/dashboard/routes/dashboard/settings/inbox/SmtpSettings.vue b/app/javascript/dashboard/routes/dashboard/settings/inbox/SmtpSettings.vue index 009e3cbb3..21a8955ef 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/inbox/SmtpSettings.vue +++ b/app/javascript/dashboard/routes/dashboard/settings/inbox/SmtpSettings.vue @@ -57,6 +57,18 @@ :placeholder="$t('INBOX_MGMT.SMTP.DOMAIN.PLACE_HOLDER')" @blur="$v.domain.$touch" /> + + +
+ +
+
+ + +
+
+
+ + + + + diff --git a/app/javascript/dashboard/routes/dashboard/settings/inbox/components/SingleSelectDropdown.vue b/app/javascript/dashboard/routes/dashboard/settings/inbox/components/SingleSelectDropdown.vue new file mode 100644 index 000000000..850b5a863 --- /dev/null +++ b/app/javascript/dashboard/routes/dashboard/settings/inbox/components/SingleSelectDropdown.vue @@ -0,0 +1,44 @@ + + diff --git a/app/mailers/conversation_reply_mailer_helper.rb b/app/mailers/conversation_reply_mailer_helper.rb index a9feb0c72..a8e5695f4 100644 --- a/app/mailers/conversation_reply_mailer_helper.rb +++ b/app/mailers/conversation_reply_mailer_helper.rb @@ -29,7 +29,9 @@ module ConversationReplyMailerHelper user_name: @channel.smtp_email, password: @channel.smtp_password, domain: @channel.smtp_domain, + tls: @channel.smtp_enable_ssl_tls, enable_starttls_auto: @channel.smtp_enable_starttls_auto, + openssl_verify_mode: @channel.smtp_openssl_verify_mode, authentication: @channel.smtp_authentication } diff --git a/app/models/channel/email.rb b/app/models/channel/email.rb index 092a9a101..3a25c3798 100644 --- a/app/models/channel/email.rb +++ b/app/models/channel/email.rb @@ -16,8 +16,10 @@ # smtp_authentication :string default("login") # smtp_domain :string default("") # smtp_email :string default("") +# smtp_enable_ssl_tls :boolean default(FALSE) # smtp_enable_starttls_auto :boolean default(TRUE) # smtp_enabled :boolean default(FALSE) +# smtp_openssl_verify_mode :string default("none") # smtp_password :string default("") # smtp_port :integer default(0) # created_at :datetime not null @@ -35,7 +37,8 @@ class Channel::Email < ApplicationRecord self.table_name = 'channel_email' EDITABLE_ATTRS = [:email, :imap_enabled, :imap_email, :imap_password, :imap_address, :imap_port, :imap_enable_ssl, :imap_inbox_synced_at, - :smtp_enabled, :smtp_email, :smtp_password, :smtp_address, :smtp_port, :smtp_domain].freeze + :smtp_enabled, :smtp_email, :smtp_password, :smtp_address, :smtp_port, :smtp_domain, :smtp_enable_starttls_auto, + :smtp_enable_ssl_tls, :smtp_openssl_verify_mode].freeze validates :email, uniqueness: true validates :forward_to_email, uniqueness: true diff --git a/app/views/api/v1/models/_inbox.json.jbuilder b/app/views/api/v1/models/_inbox.json.jbuilder index 587dfd71c..671d52072 100644 --- a/app/views/api/v1/models/_inbox.json.jbuilder +++ b/app/views/api/v1/models/_inbox.json.jbuilder @@ -67,6 +67,9 @@ if resource.email? json.smtp_port resource.channel.try(:smtp_port) json.smtp_enabled resource.channel.try(:smtp_enabled) json.smtp_domain resource.channel.try(:smtp_domain) + json.smtp_enable_ssl_tls resource.channel.try(:smtp_enable_ssl_tls) + json.smtp_enable_starttls_auto resource.channel.try(:smtp_enable_starttls_auto) + json.smtp_openssl_verify_mode resource.channel.try(:smtp_openssl_verify_mode) end ## API Channel Attributes diff --git a/db/migrate/20220116103902_add_open_ssl_verify_mode_to_channel_email.rb b/db/migrate/20220116103902_add_open_ssl_verify_mode_to_channel_email.rb new file mode 100644 index 000000000..77cddc7c5 --- /dev/null +++ b/db/migrate/20220116103902_add_open_ssl_verify_mode_to_channel_email.rb @@ -0,0 +1,8 @@ +class AddOpenSslVerifyModeToChannelEmail < ActiveRecord::Migration[6.1] + def change + change_table :channel_email, bulk: true do |t| + t.string :smtp_openssl_verify_mode, default: 'none' + t.boolean :smtp_enable_ssl_tls, default: false + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 081dbdc3a..5c16d1e87 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -202,6 +202,8 @@ ActiveRecord::Schema.define(version: 2022_01_31_081750) do t.string "smtp_domain", default: "" t.boolean "smtp_enable_starttls_auto", default: true t.string "smtp_authentication", default: "login" + t.string "smtp_openssl_verify_mode", default: "none" + t.boolean "smtp_enable_ssl_tls", default: false t.index ["email"], name: "index_channel_email_on_email", unique: true t.index ["forward_to_email"], name: "index_channel_email_on_forward_to_email", unique: true end diff --git a/spec/controllers/api/v1/accounts/conversations_controller_spec.rb b/spec/controllers/api/v1/accounts/conversations_controller_spec.rb index 56b82484f..8a874fc56 100644 --- a/spec/controllers/api/v1/accounts/conversations_controller_spec.rb +++ b/spec/controllers/api/v1/accounts/conversations_controller_spec.rb @@ -314,6 +314,7 @@ RSpec.describe 'Conversations API', type: :request do context 'when it is an authenticated user' do let(:agent) { create(:user, account: account, role: :agent) } + let(:administrator) { create(:user, account: account, role: :administrator) } before do create(:inbox_member, user: agent, inbox: conversation.inbox) @@ -341,6 +342,27 @@ RSpec.describe 'Conversations API', type: :request do expect(conversation.reload.status).to eq('open') end + it 'self assign if agent changes the conversation status to open' do + conversation.update!(status: 'pending') + post "/api/v1/accounts/#{account.id}/conversations/#{conversation.display_id}/toggle_status", + headers: agent.create_new_auth_token, + as: :json + expect(response).to have_http_status(:success) + expect(conversation.reload.status).to eq('open') + expect(conversation.reload.assignee_id).to eq(agent.id) + end + + it 'disbale self assign if admin changes the conversation status to open' do + conversation.update!(status: 'pending') + conversation.update!(assignee_id: nil) + post "/api/v1/accounts/#{account.id}/conversations/#{conversation.display_id}/toggle_status", + headers: administrator.create_new_auth_token, + as: :json + expect(response).to have_http_status(:success) + expect(conversation.reload.status).to eq('open') + expect(conversation.reload.assignee_id).not_to eq(administrator.id) + end + it 'toggles the conversation status to specific status when parameter is passed' do expect(conversation.status).to eq('open') diff --git a/spec/controllers/api/v1/accounts/inboxes_controller_spec.rb b/spec/controllers/api/v1/accounts/inboxes_controller_spec.rb index 5dc184b8c..7ebe8434a 100644 --- a/spec/controllers/api/v1/accounts/inboxes_controller_spec.rb +++ b/spec/controllers/api/v1/accounts/inboxes_controller_spec.rb @@ -437,32 +437,6 @@ RSpec.describe 'Inboxes API', type: :request do expect(email_channel.reload.imap_port).to eq(993) end - it 'updates email inbox with smtp when administrator' do - email_channel = create(:channel_email, account: account) - email_inbox = create(:inbox, channel: email_channel, account: account) - - smtp_connection = double - allow(smtp_connection).to receive(:finish).and_return(true) - allow(Net::SMTP).to receive(:start).and_return(smtp_connection) - - patch "/api/v1/accounts/#{account.id}/inboxes/#{email_inbox.id}", - headers: admin.create_new_auth_token, - params: { - channel: { - smtp_enabled: true, - smtp_address: 'smtp.gmail.com', - smtp_port: 587, - smtp_email: 'smtptest@gmail.com' - } - }, - as: :json - - expect(response).to have_http_status(:success) - expect(email_channel.reload.smtp_enabled).to be true - expect(email_channel.reload.smtp_address).to eq('smtp.gmail.com') - expect(email_channel.reload.smtp_port).to eq(587) - end - it 'updates avatar when administrator' do # no avatar before upload expect(inbox.avatar.attached?).to eq(false) @@ -501,6 +475,72 @@ RSpec.describe 'Inboxes API', type: :request do expect(inbox.reload.allow_messages_after_resolved).to be_falsey end end + + context 'when an authenticated user updates email inbox' do + let(:admin) { create(:user, account: account, role: :administrator) } + let(:email_channel) { create(:channel_email, account: account) } + let(:email_inbox) { create(:inbox, channel: email_channel, account: account) } + + it 'updates smtp configuration with starttls encryption' do + smtp_connection = double + allow(smtp_connection).to receive(:start).and_return(true) + allow(smtp_connection).to receive(:finish).and_return(true) + allow(smtp_connection).to receive(:respond_to?).and_return(true) + allow(smtp_connection).to receive(:enable_starttls_auto).and_return(true) + allow(Net::SMTP).to receive(:new).and_return(smtp_connection) + + patch "/api/v1/accounts/#{account.id}/inboxes/#{email_inbox.id}", + headers: admin.create_new_auth_token, + params: { + channel: { + smtp_enabled: true, + smtp_address: 'smtp.gmail.com', + smtp_port: 587, + smtp_email: 'smtptest@gmail.com', + smtp_enable_starttls_auto: true, + smtp_openssl_verify_mode: 'peer' + } + }, + as: :json + + expect(response).to have_http_status(:success) + expect(email_channel.reload.smtp_enabled).to be true + expect(email_channel.reload.smtp_address).to eq('smtp.gmail.com') + expect(email_channel.reload.smtp_port).to eq(587) + expect(email_channel.reload.smtp_enable_starttls_auto).to be true + expect(email_channel.reload.smtp_openssl_verify_mode).to eq('peer') + end + + it 'updates smtp configuration with ssl/tls encryption' do + smtp_connection = double + allow(smtp_connection).to receive(:start).and_return(true) + allow(smtp_connection).to receive(:finish).and_return(true) + allow(smtp_connection).to receive(:respond_to?).and_return(true) + allow(smtp_connection).to receive(:enable_tls).and_return(true) + allow(Net::SMTP).to receive(:new).and_return(smtp_connection) + + patch "/api/v1/accounts/#{account.id}/inboxes/#{email_inbox.id}", + headers: admin.create_new_auth_token, + params: { + channel: { + smtp_enabled: true, + smtp_address: 'smtp.gmail.com', + smtp_email: 'smtptest@gmail.com', + smtp_port: 587, + smtp_enable_ssl_tls: true, + smtp_openssl_verify_mode: 'none' + } + }, + as: :json + + expect(response).to have_http_status(:success) + expect(email_channel.reload.smtp_enabled).to be true + expect(email_channel.reload.smtp_address).to eq('smtp.gmail.com') + expect(email_channel.reload.smtp_port).to eq(587) + expect(email_channel.reload.smtp_enable_ssl_tls).to be true + expect(email_channel.reload.smtp_openssl_verify_mode).to eq('none') + end + end end describe 'GET /api/v1/accounts/{account.id}/inboxes/{inbox.id}/agent_bot' do