مراية لـ
https://github.com/postalserver/postal.git
تم المزامنة 2025-12-01 05:43:04 +00:00
Add IP-based SMTP authentication (#1149)
هذا الالتزام موجود في:
ملتزم من قبل
GitHub
الأصل
5c7802d218
التزام
9a3d568b27
@@ -53,3 +53,21 @@ $ ->
|
||||
$(element, $link.parent()).toggle()
|
||||
false
|
||||
|
||||
toggleCredentialInputs = (type)->
|
||||
$('[data-credential-key-type]').hide()
|
||||
$('[data-credential-key-type] input').attr('disabled', true)
|
||||
if type == 'SMTP-IP'
|
||||
$('[data-credential-key-type=smtp-ip]').show()
|
||||
$('[data-credential-key-type=smtp-ip] input').attr('disabled', false)
|
||||
else
|
||||
$('[data-credential-key-type=all]').show()
|
||||
|
||||
|
||||
$(document).on 'change', 'select#credential_type', ->
|
||||
value = $(this).val()
|
||||
toggleCredentialInputs(value)
|
||||
|
||||
$(document).on 'turbolinks:load', ->
|
||||
credentialTypeInput = $('select#credential_type')
|
||||
if credentialTypeInput.length
|
||||
toggleCredentialInputs(credentialTypeInput.val())
|
||||
|
||||
@@ -95,6 +95,10 @@
|
||||
background-color:$turquoise;
|
||||
}
|
||||
|
||||
.label--credentialType-smtp_ip {
|
||||
background-color:$orange;
|
||||
}
|
||||
|
||||
.label--spamStatus-not_checked {
|
||||
background:#aaa;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ class CredentialsController < ApplicationController
|
||||
include WithinOrganization
|
||||
|
||||
before_action { @server = organization.servers.present.find_by_permalink!(params[:server_id]) }
|
||||
before_action { params[:id] && @credential = @server.credentials.find_by_key!(params[:id]) }
|
||||
before_action { params[:id] && @credential = @server.credentials.find_by_uuid!(params[:id]) }
|
||||
|
||||
def index
|
||||
@credentials = @server.credentials.order(:name).to_a
|
||||
@@ -14,7 +14,7 @@ class CredentialsController < ApplicationController
|
||||
end
|
||||
|
||||
def create
|
||||
@credential = @server.credentials.build(params.require(:credential).permit(:type, :name, :hold))
|
||||
@credential = @server.credentials.build(params.require(:credential).permit(:type, :name, :key, :hold))
|
||||
if @credential.save
|
||||
redirect_to_with_json [organization, @server, :credentials]
|
||||
else
|
||||
@@ -23,7 +23,7 @@ class CredentialsController < ApplicationController
|
||||
end
|
||||
|
||||
def update
|
||||
if @credential.update(params.require(:credential).permit(:name, :hold))
|
||||
if @credential.update(params.require(:credential).permit(:name, :key, :hold))
|
||||
redirect_to_with_json [organization, @server, :credentials]
|
||||
else
|
||||
render_form_errors 'edit', @credential
|
||||
|
||||
@@ -12,24 +12,37 @@
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# hold :boolean default(FALSE)
|
||||
# uuid :string(255)
|
||||
#
|
||||
|
||||
class Credential < ApplicationRecord
|
||||
|
||||
include HasUUID
|
||||
|
||||
belongs_to :server
|
||||
|
||||
TYPES = ['SMTP', 'API']
|
||||
TYPES = ['SMTP', 'API', 'SMTP-IP']
|
||||
|
||||
validates :key, :presence => true, :uniqueness => true
|
||||
validates :type, :inclusion => {:in => TYPES}
|
||||
validates :name, :presence => true
|
||||
|
||||
random_string :key, :type => :chars, :length => 24, :unique => true
|
||||
validate :validate_key_cannot_be_changed
|
||||
validate :validate_key_for_smtp_ip
|
||||
|
||||
serialize :options, Hash
|
||||
|
||||
before_validation :generate_key
|
||||
|
||||
|
||||
def generate_key
|
||||
return if self.type == 'SMTP-IP'
|
||||
return if self.persisted?
|
||||
|
||||
self.key = SecureRandomString.new(24)
|
||||
end
|
||||
|
||||
def to_param
|
||||
key
|
||||
uuid
|
||||
end
|
||||
|
||||
def use
|
||||
@@ -54,4 +67,30 @@ class Credential < ApplicationRecord
|
||||
Base64.encode64("\0XX\0#{self.key}").strip
|
||||
end
|
||||
|
||||
def ipaddr
|
||||
return unless type == 'SMTP-IP'
|
||||
|
||||
@ipaddr ||= IPAddr.new(self.key)
|
||||
rescue IPAddr::InvalidAddressError
|
||||
nil
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def validate_key_cannot_be_changed
|
||||
return if new_record?
|
||||
return unless key_changed?
|
||||
return if type == 'SMTP-IP'
|
||||
|
||||
errors.add :key, "cannot be changed"
|
||||
end
|
||||
|
||||
def validate_key_for_smtp_ip
|
||||
return unless type == 'SMTP-IP'
|
||||
|
||||
IPAddr.new(self.key.to_s)
|
||||
rescue IPAddr::InvalidAddressError
|
||||
errors.add :key, "must be a valid IPv4 or IPv6 address"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -15,14 +15,23 @@
|
||||
%p.fieldSet__text
|
||||
This is a friendly name so you can identify this credential later. You can enter anything
|
||||
you want here, the more descriptive the better.
|
||||
.fieldSet__field
|
||||
|
||||
.fieldSet__field{data: {credential_key_type: 'all'}}
|
||||
= f.label :key, :class => 'fieldSet__label'
|
||||
.fieldSet__input
|
||||
= f.text_field :key, :readonly => true, :class => 'input input--text input--code', :placeholder => "Automatically generated", :tabindex => 1000, :value => (@credential.new_record? ? '' : @credential.key)
|
||||
= f.text_field :key, :readonly => false, :class => 'input input--text input--code', :placeholder => "Automatically generated", :tabindex => 1000, :value => (@credential.new_record? ? '' : @credential.key)
|
||||
%p.fieldSet__text
|
||||
This is the unique key which will be used to authenticate any requests to the API or our SMTP servers.
|
||||
This is the unique key which will be used to authenticate any requests to the API or the SMTP servers.
|
||||
It will be generated randomly and cannot be changed. If you need a new token, you can create a new one and then
|
||||
delete the old one when you're ready.
|
||||
|
||||
.fieldSet__field{data: {credential_key_type: 'smtp-ip'}}
|
||||
= f.label :key, "Network", :class => 'fieldSet__label'
|
||||
.fieldSet__input
|
||||
= f.text_field :key, :class => 'input input--text input--code'
|
||||
%p.fieldSet__text
|
||||
This is the IP address or network that you wish to allow to authenticate to this mail server.
|
||||
|
||||
.fieldSet__field
|
||||
= f.label :hold, :class => 'fieldSet__label'
|
||||
.fieldSet__input
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
%li.credentialList__item
|
||||
= link_to [:edit, organization, @server, credential], :class => 'credentialList__link' do
|
||||
.credentialList__type
|
||||
%span.label{:class => "label--credentialType-#{credential.type.underscore}"}= credential.type
|
||||
%span.label{:class => "label--credentialType-#{credential.type.underscore}"}= credential.type.split('-').last
|
||||
.credentialList__properties
|
||||
%p.credentialList__name
|
||||
= credential.name
|
||||
|
||||
8
db/migrate/20200717083943_add_uuid_to_credentials.rb
Normal file
8
db/migrate/20200717083943_add_uuid_to_credentials.rb
Normal file
@@ -0,0 +1,8 @@
|
||||
class AddUUIDToCredentials < ActiveRecord::Migration[5.2]
|
||||
def change
|
||||
add_column :credentials, :uuid, :string
|
||||
Credential.find_each do |c|
|
||||
c.update_column(:uuid, SecureRandom.uuid)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -10,7 +10,7 @@
|
||||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 2018_02_16_114344) do
|
||||
ActiveRecord::Schema.define(version: 2020_07_17_083943) do
|
||||
|
||||
create_table "additional_route_endpoints", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4", force: :cascade do |t|
|
||||
t.integer "route_id"
|
||||
@@ -68,6 +68,7 @@ ActiveRecord::Schema.define(version: 2018_02_16_114344) do
|
||||
t.datetime "created_at", precision: 6
|
||||
t.datetime "updated_at", precision: 6
|
||||
t.boolean "hold", default: false
|
||||
t.string "uuid"
|
||||
end
|
||||
|
||||
create_table "domains", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4", force: :cascade do |t|
|
||||
|
||||
@@ -312,10 +312,20 @@ module Postal
|
||||
end
|
||||
|
||||
else
|
||||
# This is unaccepted mail
|
||||
# User is trying to relay but is not authenticated. Try to authenticate by IP address
|
||||
@credential = Credential.where(:type => 'SMTP-IP').all.sort_by { |c| c.ipaddr&.prefix || 0 }.reverse.find do |credential|
|
||||
credential.ipaddr.include?(@ip_address)
|
||||
end
|
||||
|
||||
if @credential
|
||||
# Retry with credential
|
||||
@credential.use
|
||||
rcpt_to(data)
|
||||
else
|
||||
'530 Authentication required'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def data(data)
|
||||
unless in_state(:rcpt_to_received)
|
||||
|
||||
المرجع في مشكلة جديدة
حظر مستخدم