Added email channel backend validation.
This commit is contained in:
parent
8c8788b0cc
commit
27081708b1
9 changed files with 176 additions and 238 deletions
|
@ -5,198 +5,6 @@ class ChannelsController < ApplicationController
|
||||||
|
|
||||||
=begin
|
=begin
|
||||||
|
|
||||||
Resource:
|
|
||||||
GET /api/v1/channels/#{id}.json
|
|
||||||
|
|
||||||
Response example 1:
|
|
||||||
|
|
||||||
{
|
|
||||||
"id":1,
|
|
||||||
"area":"Email::Account",
|
|
||||||
"group_id:": 1,
|
|
||||||
"options":{
|
|
||||||
"inbound": {
|
|
||||||
"adapter":"IMAP",
|
|
||||||
"options": {
|
|
||||||
"host":"mail.example.com",
|
|
||||||
"user":"some_user",
|
|
||||||
"password":"some_password",
|
|
||||||
"ssl":true
|
|
||||||
},
|
|
||||||
"outbound":{
|
|
||||||
"adapter":"SMTP",
|
|
||||||
"options": {
|
|
||||||
"host":"mail.example.com",
|
|
||||||
"user":"some_user",
|
|
||||||
"password":"some_password",
|
|
||||||
"start_tls":true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"active":true,
|
|
||||||
"updated_at":"2012-09-14T17:51:53Z",
|
|
||||||
"created_at":"2012-09-14T17:51:53Z",
|
|
||||||
"updated_by_id":2.
|
|
||||||
"created_by_id":2,
|
|
||||||
}
|
|
||||||
|
|
||||||
Response example 2:
|
|
||||||
|
|
||||||
{
|
|
||||||
"id":1,
|
|
||||||
"area":"Twitter::Account",
|
|
||||||
"group_id:": 1,
|
|
||||||
"options":{
|
|
||||||
"adapter":"Twitter",
|
|
||||||
"auth": {
|
|
||||||
"consumer_key":"PJ4c3dYYRtSZZZdOKo8ow",
|
|
||||||
"consumer_secret":"ggAdnJE2Al1Vv0cwwvX5bdvKOieFs0vjCIh5M8Dxk",
|
|
||||||
"oauth_token":"293437546-xxRa9g74CercnU5AvY1uQwLLGIYrV1ezYtpX8oKW",
|
|
||||||
"oauth_token_secret":"ju0E4l9OdY2Lh1iTKMymAu6XVfOaU2oGxmcbIMRZQK4",
|
|
||||||
},
|
|
||||||
"sync":{
|
|
||||||
"search":[
|
|
||||||
{
|
|
||||||
"term":"#otrs",
|
|
||||||
"type": "mixed", # optional, possible 'mixed' (default), 'recent', 'popular'
|
|
||||||
"group_id:": 1,
|
|
||||||
"limit": 1, # optional
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"term":"#zombie23",
|
|
||||||
"group_id:": 2,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"term":"#otterhub",
|
|
||||||
"group_id:": 3,
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"mentions" {
|
|
||||||
"group_id:": 4,
|
|
||||||
"limit": 100, # optional
|
|
||||||
},
|
|
||||||
"direct_messages": {
|
|
||||||
"group_id:": 4,
|
|
||||||
"limit": 1, # optional
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"active":true,
|
|
||||||
"updated_at":"2012-09-14T17:51:53Z",
|
|
||||||
"created_at":"2012-09-14T17:51:53Z",
|
|
||||||
"updated_by_id":2.
|
|
||||||
"created_by_id":2,
|
|
||||||
}
|
|
||||||
|
|
||||||
Test:
|
|
||||||
curl http://localhost/api/v1/channels/#{id}.json -v -u #{login}:#{password}
|
|
||||||
|
|
||||||
=end
|
|
||||||
|
|
||||||
def show
|
|
||||||
return if deny_if_not_role(Z_ROLENAME_ADMIN)
|
|
||||||
return if !check_access
|
|
||||||
model_show_render(Channel, params)
|
|
||||||
end
|
|
||||||
|
|
||||||
=begin
|
|
||||||
|
|
||||||
Resource:
|
|
||||||
POST /api/v1/channels.json
|
|
||||||
|
|
||||||
Payload:
|
|
||||||
{
|
|
||||||
"area":"Email::Account",
|
|
||||||
"group_id:": 1,
|
|
||||||
"options":{
|
|
||||||
"inbound":
|
|
||||||
"adapter":"IMAP",
|
|
||||||
"options":{
|
|
||||||
"host":"mail.example.com",
|
|
||||||
"user":"some_user",
|
|
||||||
"password":"some_password",
|
|
||||||
"ssl":true
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"outbound":{
|
|
||||||
"adapter":"SMTP",
|
|
||||||
"options": {
|
|
||||||
"host":"mail.example.com",
|
|
||||||
"user":"some_user",
|
|
||||||
"password":"some_password",
|
|
||||||
"start_tls":true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"active":true,
|
|
||||||
}
|
|
||||||
|
|
||||||
Response:
|
|
||||||
{
|
|
||||||
"area":"Email::Account",
|
|
||||||
...
|
|
||||||
}
|
|
||||||
|
|
||||||
Test:
|
|
||||||
curl http://localhost/api/v1/channels.json -v -u #{login}:#{password} -H "Content-Type: application/json" -X POST -d '{"name": "some_name","active": true, "note": "some note"}'
|
|
||||||
|
|
||||||
=end
|
|
||||||
|
|
||||||
def create
|
|
||||||
return if deny_if_not_role(Z_ROLENAME_ADMIN)
|
|
||||||
model_create_render(Channel, params)
|
|
||||||
end
|
|
||||||
|
|
||||||
=begin
|
|
||||||
|
|
||||||
Resource:
|
|
||||||
PUT /api/v1/channels/{id}.json
|
|
||||||
|
|
||||||
Payload:
|
|
||||||
{
|
|
||||||
"id":1,
|
|
||||||
"area":"Email::Account",
|
|
||||||
"group_id:": 1,
|
|
||||||
"options":{
|
|
||||||
"inbound":
|
|
||||||
"adapter":"IMAP",
|
|
||||||
"options":{
|
|
||||||
"host":"mail.example.com",
|
|
||||||
"user":"some_user",
|
|
||||||
"password":"some_password",
|
|
||||||
"ssl":true
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"outbound":{
|
|
||||||
"adapter":"SMTP",
|
|
||||||
"options": {
|
|
||||||
"host":"mail.example.com",
|
|
||||||
"user":"some_user",
|
|
||||||
"password":"some_password",
|
|
||||||
"start_tls":true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"active":true,
|
|
||||||
}
|
|
||||||
|
|
||||||
Response:
|
|
||||||
{
|
|
||||||
"id": 1,
|
|
||||||
"name": "some_name",
|
|
||||||
...
|
|
||||||
}
|
|
||||||
|
|
||||||
Test:
|
|
||||||
curl http://localhost/api/v1/channels.json -v -u #{login}:#{password} -H "Content-Type: application/json" -X PUT -d '{"name": "some_name","active": true, "note": "some note"}'
|
|
||||||
|
|
||||||
=end
|
|
||||||
|
|
||||||
def update
|
|
||||||
return if deny_if_not_role(Z_ROLENAME_ADMIN)
|
|
||||||
return if !check_access
|
|
||||||
model_update_render(Channel, params)
|
|
||||||
end
|
|
||||||
|
|
||||||
=begin
|
|
||||||
|
|
||||||
Resource:
|
Resource:
|
||||||
DELETE /api/v1/channels/{id}.json
|
DELETE /api/v1/channels/{id}.json
|
||||||
|
|
||||||
|
@ -405,15 +213,6 @@ curl http://localhost/api/v1/channels.json -v -u #{login}:#{password} -H "Conten
|
||||||
|
|
||||||
adapter = params[:adapter].downcase
|
adapter = params[:adapter].downcase
|
||||||
|
|
||||||
# validate adapter
|
|
||||||
if adapter !~ /^(smtp|sendmail)$/
|
|
||||||
render json: {
|
|
||||||
result: 'failed',
|
|
||||||
message: "Unknown adapter '#{adapter}'",
|
|
||||||
}
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
email = Setting.get('notification_sender')
|
email = Setting.get('notification_sender')
|
||||||
|
|
||||||
# connection test
|
# connection test
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
# Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/
|
# Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/
|
||||||
|
|
||||||
class Channel < ApplicationModel
|
class Channel < ApplicationModel
|
||||||
|
load 'channel/assets.rb'
|
||||||
|
include Channel::Assets
|
||||||
|
|
||||||
store :options
|
store :options
|
||||||
store :preferences
|
store :preferences
|
||||||
|
|
||||||
|
@ -34,9 +37,7 @@ fetch one account
|
||||||
|
|
||||||
adapter = options[:adapter]
|
adapter = options[:adapter]
|
||||||
adapter_options = options
|
adapter_options = options
|
||||||
if options[:options]
|
if options[:inbound] && options[:inbound][:adapter]
|
||||||
adapter_options = options[:options]
|
|
||||||
elsif options[:inbound] && options[:inbound][:adapter]
|
|
||||||
adapter = options[:inbound][:adapter]
|
adapter = options[:inbound][:adapter]
|
||||||
adapter_options = options[:inbound][:options]
|
adapter_options = options[:inbound][:options]
|
||||||
end
|
end
|
||||||
|
@ -79,9 +80,7 @@ send via account
|
||||||
|
|
||||||
adapter = options[:adapter]
|
adapter = options[:adapter]
|
||||||
adapter_options = options
|
adapter_options = options
|
||||||
if options[:options]
|
if options[:outbound] && options[:outbound][:adapter]
|
||||||
adapter_options = options[:options]
|
|
||||||
elsif options[:outbound] && options[:outbound][:adapter]
|
|
||||||
adapter = options[:outbound][:adapter]
|
adapter = options[:outbound][:adapter]
|
||||||
adapter_options = options[:outbound][:options]
|
adapter_options = options[:outbound][:options]
|
||||||
end
|
end
|
||||||
|
|
54
app/models/channel/assets.rb
Normal file
54
app/models/channel/assets.rb
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
# Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/
|
||||||
|
|
||||||
|
class Channel
|
||||||
|
module Assets
|
||||||
|
|
||||||
|
=begin
|
||||||
|
|
||||||
|
get all assets / related models for this channel
|
||||||
|
|
||||||
|
channel = Channel.find(123)
|
||||||
|
result = channel.assets( assets_if_exists )
|
||||||
|
|
||||||
|
returns
|
||||||
|
|
||||||
|
result = {
|
||||||
|
:channels => {
|
||||||
|
123 => channel_model_123,
|
||||||
|
1234 => channel_model_1234,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
=end
|
||||||
|
|
||||||
|
def assets (data = {})
|
||||||
|
|
||||||
|
if !data[ self.class.to_app_model ]
|
||||||
|
data[ self.class.to_app_model ] = {}
|
||||||
|
end
|
||||||
|
if !data[ self.class.to_app_model ][ id ]
|
||||||
|
attributes = attributes_with_associations
|
||||||
|
|
||||||
|
# remove passwords
|
||||||
|
%w(inbound outbound).each {|key|
|
||||||
|
if attributes['options'] && attributes['options'][key] && attributes['options'][key]['options']
|
||||||
|
attributes['options'][key]['options'].delete('password')
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
|
data[ self.class.to_app_model ][ id ] = attributes
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
return data if !self['created_by_id'] && !self['updated_by_id']
|
||||||
|
%w(created_by_id updated_by_id).each {|local_user_id|
|
||||||
|
next if !self[ local_user_id ]
|
||||||
|
next if data[ User.to_app_model ] && data[ User.to_app_model ][ self[ local_user_id ] ]
|
||||||
|
user = User.lookup( id: self[ local_user_id ] )
|
||||||
|
data = user.assets( data )
|
||||||
|
}
|
||||||
|
data
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,20 +1,44 @@
|
||||||
# Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/
|
# Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/
|
||||||
|
|
||||||
class Channel::Driver::Smtp
|
class Channel::Driver::Smtp
|
||||||
|
|
||||||
|
=begin
|
||||||
|
|
||||||
|
instance = Channel::Driver::Smtp.new
|
||||||
|
instance.send(
|
||||||
|
host: 'some.host',
|
||||||
|
port: 25,
|
||||||
|
enable_starttls_auto: true, # optional
|
||||||
|
user: 'someuser',
|
||||||
|
password: 'somepass'
|
||||||
|
)
|
||||||
|
|
||||||
|
=end
|
||||||
|
|
||||||
def send(options, attr, notification = false)
|
def send(options, attr, notification = false)
|
||||||
|
|
||||||
# return if we run import mode
|
# return if we run import mode
|
||||||
return if Setting.get('import_mode')
|
return if Setting.get('import_mode')
|
||||||
|
|
||||||
|
# set smtp defaults
|
||||||
|
if !options.key?(:port)
|
||||||
|
options[:port] = 25
|
||||||
|
end
|
||||||
|
if !options.key?(:enable_starttls_auto)
|
||||||
|
options[:enable_starttls_auto] = true
|
||||||
|
end
|
||||||
|
if !options.key?(:openssl_verify_mode)
|
||||||
|
options[:openssl_verify_mode] = 'none'
|
||||||
|
end
|
||||||
mail = Channel::EmailBuild.build(attr, notification)
|
mail = Channel::EmailBuild.build(attr, notification)
|
||||||
mail.delivery_method :smtp, {
|
mail.delivery_method :smtp, {
|
||||||
openssl_verify_mode: 'none',
|
openssl_verify_mode: options[:openssl_verify_mode],
|
||||||
address: options[:host],
|
address: options[:host],
|
||||||
port: options[:port] || 25,
|
port: options[:port],
|
||||||
domain: options[:host],
|
domain: options[:host],
|
||||||
user_name: options[:user],
|
user_name: options[:user],
|
||||||
password: options[:password],
|
password: options[:password],
|
||||||
enable_starttls_auto: true,
|
enable_starttls_auto: options[:enable_starttls_auto],
|
||||||
}
|
}
|
||||||
mail.deliver
|
mail.deliver
|
||||||
end
|
end
|
||||||
|
|
|
@ -10,9 +10,6 @@ Zammad::Application.routes.draw do
|
||||||
match api_path + '/channels/email_notification', to: 'channels#email_notification', via: :post
|
match api_path + '/channels/email_notification', to: 'channels#email_notification', via: :post
|
||||||
|
|
||||||
# channels
|
# channels
|
||||||
match api_path + '/channels/:id', to: 'channels#show', via: :get
|
|
||||||
match api_path + '/channels', to: 'channels#create', via: :post
|
|
||||||
match api_path + '/channels/:id', to: 'channels#update', via: :put
|
|
||||||
match api_path + '/channels/:id', to: 'channels#destroy', via: :delete
|
match api_path + '/channels/:id', to: 'channels#destroy', via: :delete
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,6 +2,34 @@ module EmailHelper
|
||||||
|
|
||||||
=begin
|
=begin
|
||||||
|
|
||||||
|
get available driver
|
||||||
|
|
||||||
|
result = EmailHelper.available_driver
|
||||||
|
|
||||||
|
returns
|
||||||
|
|
||||||
|
{
|
||||||
|
:inbound => ['imap', 'pop3'],
|
||||||
|
:outbound => ['smtp', 'sendmail'],
|
||||||
|
}
|
||||||
|
|
||||||
|
=end
|
||||||
|
|
||||||
|
def self.available_driver
|
||||||
|
if Setting.get('system_online_service')
|
||||||
|
return {
|
||||||
|
:inbound => ['imap', 'pop3'],
|
||||||
|
:outbound => ['smtp'],
|
||||||
|
}
|
||||||
|
end
|
||||||
|
{
|
||||||
|
:inbound => ['imap', 'pop3'],
|
||||||
|
:outbound => ['smtp', 'sendmail'],
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
=begin
|
||||||
|
|
||||||
get mail parts
|
get mail parts
|
||||||
|
|
||||||
user, domain = EmailHelper.parse_email('somebody@example.com')
|
user, domain = EmailHelper.parse_email('somebody@example.com')
|
||||||
|
|
|
@ -188,14 +188,17 @@ returns on fail
|
||||||
|
|
||||||
adapter = params[:adapter].downcase
|
adapter = params[:adapter].downcase
|
||||||
|
|
||||||
|
# validate adapter
|
||||||
|
if !EmailHelper.available_driver[:inbound].include?(adapter)
|
||||||
|
return {
|
||||||
|
result: 'failed',
|
||||||
|
message: "Unknown adapter '#{adapter}'",
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
# connection test
|
# connection test
|
||||||
begin
|
begin
|
||||||
|
|
||||||
# validate adapter
|
|
||||||
if adapter !~ /^(imap|pop3)$/
|
|
||||||
fail "Unknown adapter '#{adapter}'"
|
|
||||||
end
|
|
||||||
|
|
||||||
require "channel/driver/#{adapter.to_filename}"
|
require "channel/driver/#{adapter.to_filename}"
|
||||||
|
|
||||||
driver_class = Object.const_get("Channel::Driver::#{adapter.to_classname}")
|
driver_class = Object.const_get("Channel::Driver::#{adapter.to_classname}")
|
||||||
|
@ -264,6 +267,14 @@ returns on fail
|
||||||
|
|
||||||
adapter = params[:adapter].downcase
|
adapter = params[:adapter].downcase
|
||||||
|
|
||||||
|
# validate adapter
|
||||||
|
if !EmailHelper.available_driver[:outbound].include?(adapter)
|
||||||
|
return {
|
||||||
|
result: 'failed',
|
||||||
|
message: "Unknown adapter '#{adapter}'",
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
# prepare test email
|
# prepare test email
|
||||||
if subject
|
if subject
|
||||||
mail = {
|
mail = {
|
||||||
|
@ -288,21 +299,6 @@ returns on fail
|
||||||
# test connection
|
# test connection
|
||||||
begin
|
begin
|
||||||
|
|
||||||
# validate adapter
|
|
||||||
if adapter !~ /^(smtp|sendmail)$/
|
|
||||||
fail "Unknown adapter '#{adapter}'"
|
|
||||||
end
|
|
||||||
|
|
||||||
# set smtp defaults
|
|
||||||
if adapter =~ /^smtp$/
|
|
||||||
if !params[:options].key?(:port)
|
|
||||||
params[:options][:port] = 25
|
|
||||||
end
|
|
||||||
if !params[:options].key?(:ssl)
|
|
||||||
params[:options][:ssl] = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
require "channel/driver/#{adapter.to_filename}"
|
require "channel/driver/#{adapter.to_filename}"
|
||||||
|
|
||||||
driver_class = Object.const_get("Channel::Driver::#{adapter.to_classname}")
|
driver_class = Object.const_get("Channel::Driver::#{adapter.to_classname}")
|
||||||
|
|
|
@ -67,6 +67,16 @@ or
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# validate adapter
|
||||||
|
adapter = params[:inbound][:adapter].downcase
|
||||||
|
if !EmailHelper.available_driver[:inbound].include?(adapter)
|
||||||
|
return {
|
||||||
|
result: 'failed',
|
||||||
|
message: "Unknown adapter '#{adapter}'",
|
||||||
|
}
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
# looking for verify email
|
# looking for verify email
|
||||||
(1..10).each {
|
(1..10).each {
|
||||||
sleep 5
|
sleep 5
|
||||||
|
@ -75,11 +85,11 @@ or
|
||||||
found = nil
|
found = nil
|
||||||
|
|
||||||
begin
|
begin
|
||||||
if params[:inbound][:adapter] =~ /^imap$/i
|
require "channel/driver/#{adapter.to_filename}"
|
||||||
found = Channel::Driver::Imap.new.fetch(params[:inbound][:options], self, 'verify', subject)
|
|
||||||
else
|
driver_class = Object.const_get("Channel::Driver::#{adapter.to_classname}")
|
||||||
found = Channel::Driver::Pop3.new.fetch(params[:inbound][:options], self, 'verify', subject)
|
driver_instance = driver_class.new
|
||||||
end
|
found = driver_instance.fetch(params[:inbound][:options], self, 'verify', subject)
|
||||||
rescue => e
|
rescue => e
|
||||||
result = {
|
result = {
|
||||||
result: 'invalid',
|
result: 'invalid',
|
||||||
|
|
|
@ -139,6 +139,20 @@ class EmailHelperTest < ActiveSupport::TestCase
|
||||||
|
|
||||||
test 'z probe_inbound' do
|
test 'z probe_inbound' do
|
||||||
|
|
||||||
|
# invalid adapter
|
||||||
|
result = EmailHelper::Probe.inbound(
|
||||||
|
adapter: 'imap2',
|
||||||
|
options: {
|
||||||
|
host: 'not_existsing_host',
|
||||||
|
port: 993,
|
||||||
|
ssl: true,
|
||||||
|
user: 'some@example.com',
|
||||||
|
password: 'password',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert_equal('failed', result[:result])
|
||||||
|
|
||||||
# network issues
|
# network issues
|
||||||
result = EmailHelper::Probe.inbound(
|
result = EmailHelper::Probe.inbound(
|
||||||
adapter: 'imap',
|
adapter: 'imap',
|
||||||
|
@ -256,6 +270,23 @@ class EmailHelperTest < ActiveSupport::TestCase
|
||||||
|
|
||||||
test 'z probe_outbound' do
|
test 'z probe_outbound' do
|
||||||
|
|
||||||
|
# invalid adapter
|
||||||
|
result = EmailHelper::Probe.outbound(
|
||||||
|
{
|
||||||
|
adapter: 'smtp2',
|
||||||
|
options: {
|
||||||
|
host: 'not_existsing_host',
|
||||||
|
port: 25,
|
||||||
|
start_tls: true,
|
||||||
|
user: 'some@example.com',
|
||||||
|
password: 'password',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'some@example.com',
|
||||||
|
)
|
||||||
|
|
||||||
|
assert_equal('failed', result[:result])
|
||||||
|
|
||||||
# network issues
|
# network issues
|
||||||
result = EmailHelper::Probe.outbound(
|
result = EmailHelper::Probe.outbound(
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue