Allow signups to be blocked by MX host
authorTom Hughes <tom@compton.nu>
Sun, 23 Jun 2019 10:21:03 +0000 (11:21 +0100)
committerTom Hughes <tom@compton.nu>
Sun, 23 Jun 2019 10:21:03 +0000 (11:21 +0100)
app/controllers/users_controller.rb
app/models/acl.rb
db/migrate/20190623093642_add_mx_acls.rb [new file with mode: 0644]
db/structure.sql
test/models/acl_test.rb

index 1ec9ab8..1703184 100644 (file)
@@ -752,7 +752,13 @@ class UsersController < ApplicationController
                email.split("@").last
              end
 
-    if blocked = Acl.no_account_creation(request.remote_ip, domain)
+    mx_servers = if domain.nil?
+                   nil
+                 else
+                   domain_mx_servers(domain)
+                 end
+
+    if blocked = Acl.no_account_creation(request.remote_ip, :domain => domain, :mx => mx_servers)
       logger.info "Blocked signup from #{request.remote_ip} for #{email}"
 
       render :action => "blocked"
@@ -761,6 +767,14 @@ class UsersController < ApplicationController
     !blocked
   end
 
+  ##
+  # get list of MX servers for a domains
+  def domain_mx_servers(domain)
+    Resolv::DNS.open do |dns|
+      dns.getresources(domain, Resolv::DNS::Resource::IN::MX).collect(&:exchange).collect(&:to_s)
+    end
+  end
+
   ##
   # check if this user has a gravatar and set the user pref is true
   def gravatar_enable(user)
index cf83e67..895ed61 100644 (file)
@@ -7,34 +7,37 @@
 #  k       :string           not null
 #  v       :string
 #  domain  :string
+#  mx      :string
 #
 # Indexes
 #
 #  acls_k_idx             (k)
 #  index_acls_on_address  (address) USING gist
 #  index_acls_on_domain   (domain)
+#  index_acls_on_mx       (mx)
 #
 
 class Acl < ActiveRecord::Base
   validates :k, :presence => true
 
-  def self.match(address, domain = nil)
-    if domain
-      Acl.where("address >>= ? OR domain = ?", address, domain)
-    else
-      Acl.where("address >>= ?", address)
-    end
+  def self.match(address, options = {})
+    acls = Acl.where("address >>= ?", address)
+
+    acls = acls.or(Acl.where(:domain => options[:domain])) if options[:domain]
+    acls = acls.or(Acl.where(:mx => options[:mx])) if options[:mx]
+
+    acls
   end
 
-  def self.no_account_creation(address, domain = nil)
-    match(address, domain).where(:k => "no_account_creation").exists?
+  def self.no_account_creation(address, options = {})
+    match(address, options).where(:k => "no_account_creation").exists?
   end
 
   def self.no_note_comment(address, domain = nil)
-    match(address, domain).where(:k => "no_note_comment").exists?
+    match(address, :domain => domain).where(:k => "no_note_comment").exists?
   end
 
   def self.no_trace_download(address, domain = nil)
-    match(address, domain).where(:k => "no_trace_download").exists?
+    match(address, :domain => domain).where(:k => "no_trace_download").exists?
   end
 end
diff --git a/db/migrate/20190623093642_add_mx_acls.rb b/db/migrate/20190623093642_add_mx_acls.rb
new file mode 100644 (file)
index 0000000..754e2ee
--- /dev/null
@@ -0,0 +1,9 @@
+class AddMxAcls < ActiveRecord::Migration[5.2]
+  def change
+    add_column :acls, :mx, :string
+
+    safety_assured do
+      add_index :acls, :mx
+    end
+  end
+end
index 0818ae0..d115140 100644 (file)
@@ -168,7 +168,8 @@ CREATE TABLE public.acls (
     address inet,
     k character varying NOT NULL,
     v character varying,
-    domain character varying
+    domain character varying,
+    mx character varying
 );
 
 
@@ -2078,6 +2079,13 @@ CREATE INDEX index_acls_on_address ON public.acls USING gist (address inet_ops);
 CREATE INDEX index_acls_on_domain ON public.acls USING btree (domain);
 
 
+--
+-- Name: index_acls_on_mx; Type: INDEX; Schema: public; Owner: -
+--
+
+CREATE INDEX index_acls_on_mx ON public.acls USING btree (mx);
+
+
 --
 -- Name: index_changeset_comments_on_created_at; Type: INDEX; Schema: public; Owner: -
 --
@@ -2949,6 +2957,7 @@ INSERT INTO "schema_migrations" (version) VALUES
 ('20181020114000'),
 ('20181031113522'),
 ('20190518115041'),
+('20190623093642'),
 ('21'),
 ('22'),
 ('23'),
index d3d8f26..ad17fc1 100644 (file)
@@ -15,8 +15,14 @@ class AclTest < ActiveSupport::TestCase
   end
 
   def test_no_account_creation_by_domain
-    assert_not Acl.no_account_creation("192.168.1.1", "example.com")
+    assert_not Acl.no_account_creation("192.168.1.1", :domain => "example.com")
     create(:acl, :domain => "example.com", :k => "no_account_creation")
-    assert Acl.no_account_creation("192.168.1.1", "example.com")
+    assert Acl.no_account_creation("192.168.1.1", :domain => "example.com")
+  end
+
+  def test_no_account_creation_by_mx
+    assert_not Acl.no_account_creation("192.168.1.1", :mx => "mail.example.com")
+    create(:acl, :mx => "mail.example.com", :k => "no_account_creation")
+    assert Acl.no_account_creation("192.168.1.1", :mx => "mail.example.com")
   end
 end