about summary refs log tree commit diff
path: root/app/models
diff options
context:
space:
mode:
authorreverite <samantha@chalker.io>2019-03-15 05:54:30 -0700
committerreverite <samantha@chalker.io>2019-03-15 05:54:30 -0700
commit75eeb003b09c53d3b4e98046d1c20b0ad8a887bb (patch)
treed2ae669ee583c613a474f8698b7ea718da803819 /app/models
parent41d1369391d70a9cd25bdf96cfe567975793ef5a (diff)
parentc2fa0f7c40bcc4064e8baaa221665eadd391c001 (diff)
Merge remote-tracking branch 'glitch/master' into production
Diffstat (limited to 'app/models')
-rw-r--r--app/models/account.rb7
-rw-r--r--app/models/account_filter.rb4
-rw-r--r--app/models/concerns/expireable.rb6
-rw-r--r--app/models/concerns/ldap_authenticable.rb25
-rw-r--r--app/models/concerns/omniauthable.rb2
-rw-r--r--app/models/concerns/pam_authenticable.rb68
-rw-r--r--app/models/concerns/user_roles.rb54
-rw-r--r--app/models/form/admin_settings.rb4
-rw-r--r--app/models/notification.rb8
-rw-r--r--app/models/poll.rb2
-rw-r--r--app/models/tag.rb8
-rw-r--r--app/models/user.rb166
12 files changed, 227 insertions, 127 deletions
diff --git a/app/models/account.rb b/app/models/account.rb
index 79eecc306..6b539f004 100644
--- a/app/models/account.rb
+++ b/app/models/account.rb
@@ -108,6 +108,8 @@ class Account < ApplicationRecord
            :current_sign_in_ip,
            :current_sign_in_at,
            :confirmed?,
+           :approved?,
+           :pending?,
            :admin?,
            :moderator?,
            :staff?,
@@ -474,6 +476,7 @@ class Account < ApplicationRecord
 
   before_create :generate_keys
   before_validation :prepare_contents, if: :local?
+  before_validation :prepare_username, on: :create
   before_destroy :clean_feed_manager
 
   private
@@ -483,6 +486,10 @@ class Account < ApplicationRecord
     note&.strip!
   end
 
+  def prepare_username
+    username&.squish!
+  end
+
   def generate_keys
     return unless local? && !Rails.env.test?
 
diff --git a/app/models/account_filter.rb b/app/models/account_filter.rb
index b10f50db7..d2503100c 100644
--- a/app/models/account_filter.rb
+++ b/app/models/account_filter.rb
@@ -22,7 +22,7 @@ class AccountFilter
 
   def set_defaults!
     params['local']  = '1' if params['remote'].blank?
-    params['active'] = '1' if params['suspended'].blank? && params['silenced'].blank?
+    params['active'] = '1' if params['suspended'].blank? && params['silenced'].blank? && params['pending'].blank?
   end
 
   def scope_for(key, value)
@@ -35,6 +35,8 @@ class AccountFilter
       Account.where(domain: value)
     when 'active'
       Account.without_suspended
+    when 'pending'
+      accounts_with_users.merge User.pending
     when 'silenced'
       Account.silenced
     when 'suspended'
diff --git a/app/models/concerns/expireable.rb b/app/models/concerns/expireable.rb
index 2c0631476..f7d2bab49 100644
--- a/app/models/concerns/expireable.rb
+++ b/app/models/concerns/expireable.rb
@@ -18,7 +18,11 @@ module Expireable
     end
 
     def expired?
-      !expires_at.nil? && expires_at < Time.now.utc
+      expires? && expires_at < Time.now.utc
+    end
+
+    def expires?
+      !expires_at.nil?
     end
   end
 end
diff --git a/app/models/concerns/ldap_authenticable.rb b/app/models/concerns/ldap_authenticable.rb
new file mode 100644
index 000000000..e1b5e3832
--- /dev/null
+++ b/app/models/concerns/ldap_authenticable.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+module LdapAuthenticable
+  extend ActiveSupport::Concern
+
+  def ldap_setup(_attributes)
+    self.confirmed_at = Time.now.utc
+    self.admin        = false
+
+    save!
+  end
+
+  class_methods do
+    def ldap_get_user(attributes = {})
+      resource = joins(:account).find_by(accounts: { username: attributes[Devise.ldap_uid.to_sym].first })
+
+      if resource.blank?
+        resource = new(email: attributes[:mail].first, agreement: true, account_attributes: { username: attributes[Devise.ldap_uid.to_sym].first })
+        resource.ldap_setup(attributes)
+      end
+
+      resource
+    end
+  end
+end
diff --git a/app/models/concerns/omniauthable.rb b/app/models/concerns/omniauthable.rb
index 4dd2e9383..1b28b8162 100644
--- a/app/models/concerns/omniauthable.rb
+++ b/app/models/concerns/omniauthable.rb
@@ -7,6 +7,8 @@ module Omniauthable
   TEMP_EMAIL_REGEX = /\Achange@me/
 
   included do
+    devise :omniauthable
+
     def omniauth_providers
       Devise.omniauth_configs.keys
     end
diff --git a/app/models/concerns/pam_authenticable.rb b/app/models/concerns/pam_authenticable.rb
new file mode 100644
index 000000000..2f651c1a3
--- /dev/null
+++ b/app/models/concerns/pam_authenticable.rb
@@ -0,0 +1,68 @@
+# frozen_string_literal: true
+
+module PamAuthenticable
+  extend ActiveSupport::Concern
+
+  included do
+    devise :pam_authenticatable if ENV['PAM_ENABLED'] == 'true'
+
+    def pam_conflict(_attributes)
+      # Block pam login tries on traditional account
+    end
+
+    def pam_conflict?
+      if Devise.pam_authentication
+        encrypted_password.present? && pam_managed_user?
+      else
+        false
+      end
+    end
+
+    def pam_get_name
+      if account.present?
+        account.username
+      else
+        super
+      end
+    end
+
+    def pam_setup(_attributes)
+      account = Account.new(username: pam_get_name)
+      account.save!(validate: false)
+
+      self.email        = "#{account.username}@#{find_pam_suffix}" if email.nil? && find_pam_suffix
+      self.confirmed_at = Time.now.utc
+      self.admin        = false
+      self.account      = account
+
+      account.destroy! unless save
+    end
+
+    def self.pam_get_user(attributes = {})
+      return nil unless attributes[:email]
+
+      resource = begin
+        if Devise.check_at_sign && !attributes[:email].index('@')
+          joins(:account).find_by(accounts: { username: attributes[:email] })
+        else
+          find_by(email: attributes[:email])
+        end
+      end
+
+      if resource.nil?
+        resource = new(email: attributes[:email], agreement: true)
+
+        if Devise.check_at_sign && !resource[:email].index('@')
+          resource[:email] = Rpam2.getenv(resource.find_pam_service, attributes[:email], attributes[:password], 'email', false)
+          resource[:email] = "#{attributes[:email]}@#{resource.find_pam_suffix}" unless resource[:email]
+        end
+      end
+
+      resource
+    end
+
+    def self.authenticate_with_pam(attributes = {})
+      super if Devise.pam_authentication
+    end
+  end
+end
diff --git a/app/models/concerns/user_roles.rb b/app/models/concerns/user_roles.rb
new file mode 100644
index 000000000..58dffdc46
--- /dev/null
+++ b/app/models/concerns/user_roles.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+
+module UserRoles
+  extend ActiveSupport::Concern
+
+  included do
+    scope :admins, -> { where(admin: true) }
+    scope :moderators, -> { where(moderator: true) }
+    scope :staff, -> { admins.or(moderators) }
+  end
+
+  def staff?
+    admin? || moderator?
+  end
+
+  def role
+    if admin?
+      'admin'
+    elsif moderator?
+      'moderator'
+    else
+      'user'
+    end
+  end
+
+  def role?(role)
+    case role
+    when 'user'
+      true
+    when 'moderator'
+      staff?
+    when 'admin'
+      admin?
+    else
+      false
+    end
+  end
+
+  def promote!
+    if moderator?
+      update!(moderator: false, admin: true)
+    elsif !admin?
+      update!(moderator: true)
+    end
+  end
+
+  def demote!
+    if admin?
+      update!(admin: false, moderator: true)
+    elsif moderator?
+      update!(moderator: false)
+    end
+  end
+end
diff --git a/app/models/form/admin_settings.rb b/app/models/form/admin_settings.rb
index d568200ed..929c65793 100644
--- a/app/models/form/admin_settings.rb
+++ b/app/models/form/admin_settings.rb
@@ -18,8 +18,8 @@ class Form::AdminSettings
     :site_extended_description=,
     :site_terms,
     :site_terms=,
-    :open_registrations,
-    :open_registrations=,
+    :registrations_mode,
+    :registrations_mode=,
     :closed_registrations_message,
     :closed_registrations_message=,
     :open_deletion,
diff --git a/app/models/notification.rb b/app/models/notification.rb
index 2f0a9b78c..982136c05 100644
--- a/app/models/notification.rb
+++ b/app/models/notification.rb
@@ -22,6 +22,7 @@ class Notification < ApplicationRecord
     follow:         'Follow',
     follow_request: 'FollowRequest',
     favourite:      'Favourite',
+    poll:           'Poll',
   }.freeze
 
   STATUS_INCLUDES = [:account, :application, :media_attachments, :tags, active_mentions: :account, reblog: [:account, :application, :media_attachments, :tags, active_mentions: :account]].freeze
@@ -35,6 +36,7 @@ class Notification < ApplicationRecord
   belongs_to :follow,         foreign_type: 'Follow',        foreign_key: 'activity_id', optional: true
   belongs_to :follow_request, foreign_type: 'FollowRequest', foreign_key: 'activity_id', optional: true
   belongs_to :favourite,      foreign_type: 'Favourite',     foreign_key: 'activity_id', optional: true
+  belongs_to :poll,           foreign_type: 'Poll',          foreign_key: 'activity_id', optional: true
 
   validates :account_id, uniqueness: { scope: [:activity_type, :activity_id] }
   validates :activity_type, inclusion: { in: TYPE_CLASS_MAP.values }
@@ -44,7 +46,7 @@ class Notification < ApplicationRecord
     where(activity_type: types)
   }
 
-  cache_associated :from_account, status: STATUS_INCLUDES, mention: [status: STATUS_INCLUDES], favourite: [:account, status: STATUS_INCLUDES], follow: :account
+  cache_associated :from_account, status: STATUS_INCLUDES, mention: [status: STATUS_INCLUDES], favourite: [:account, status: STATUS_INCLUDES], follow: :account, poll: [status: STATUS_INCLUDES]
 
   def type
     @type ||= TYPE_CLASS_MAP.invert[activity_type].to_sym
@@ -58,6 +60,8 @@ class Notification < ApplicationRecord
       favourite&.status
     when :mention
       mention&.status
+    when :poll
+      poll&.status
     end
   end
 
@@ -97,7 +101,7 @@ class Notification < ApplicationRecord
     return unless new_record?
 
     case activity_type
-    when 'Status', 'Follow', 'Favourite', 'FollowRequest'
+    when 'Status', 'Follow', 'Favourite', 'FollowRequest', 'Poll'
       self.from_account_id = activity&.account_id
     when 'Mention'
       self.from_account_id = activity&.status&.account_id
diff --git a/app/models/poll.rb b/app/models/poll.rb
index 09f0b65ec..6df230337 100644
--- a/app/models/poll.rb
+++ b/app/models/poll.rb
@@ -26,6 +26,8 @@ class Poll < ApplicationRecord
 
   has_many :votes, class_name: 'PollVote', inverse_of: :poll, dependent: :destroy
 
+  has_many :notifications, as: :activity, dependent: :destroy
+
   validates :options, presence: true
   validates :expires_at, presence: true, if: :local?
   validates_with PollValidator, on: :create, if: :local?
diff --git a/app/models/tag.rb b/app/models/tag.rb
index 788a678bd..7db76d157 100644
--- a/app/models/tag.rb
+++ b/app/models/tag.rb
@@ -72,6 +72,14 @@ class Tag < ApplicationRecord
          .limit(limit)
          .offset(offset)
     end
+
+    def find_normalized(name)
+      find_by(name: name.mb_chars.downcase.to_s)
+    end
+
+    def find_normalized!(name)
+      find_normalized(name) || raise(ActiveRecord::RecordNotFound)
+    end
   end
 
   private
diff --git a/app/models/user.rb b/app/models/user.rb
index f91ed7015..47657a670 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -37,11 +37,12 @@
 #  remember_token            :string
 #  chosen_languages          :string           is an Array
 #  created_by_application_id :bigint(8)
+#  approved                  :boolean          default(TRUE), not null
 #
 
 class User < ApplicationRecord
   include Settings::Extend
-  include Omniauthable
+  include UserRoles
 
   # The home and list feeds will be stored in Redis for this amount
   # of time, and status fan-out to followers will include only people
@@ -61,9 +62,9 @@ class User < ApplicationRecord
   devise :registerable, :recoverable, :rememberable, :trackable, :validatable,
          :confirmable
 
-  devise :pam_authenticatable if ENV['PAM_ENABLED'] == 'true'
-
-  devise :omniauthable
+  include Omniauthable
+  include PamAuthenticable
+  include LdapAuthenticable
 
   belongs_to :account, inverse_of: :user
   belongs_to :invite, counter_cache: :uses, optional: true
@@ -79,9 +80,8 @@ class User < ApplicationRecord
   validates :agreement, acceptance: { allow_nil: false, accept: [true, 'true', '1'] }, on: :create
 
   scope :recent, -> { order(id: :desc) }
-  scope :admins, -> { where(admin: true) }
-  scope :moderators, -> { where(moderator: true) }
-  scope :staff, -> { admins.or(moderators) }
+  scope :pending, -> { where(approved: false) }
+  scope :approved, -> { where(approved: true) }
   scope :confirmed, -> { where.not(confirmed_at: nil) }
   scope :enabled, -> { where(disabled: false) }
   scope :inactive, -> { where(arel_table[:current_sign_in_at].lt(ACTIVE_DURATION.ago)) }
@@ -90,6 +90,7 @@ class User < ApplicationRecord
   scope :emailable, -> { confirmed.enabled.joins(:account).merge(Account.searchable) }
 
   before_validation :sanitize_languages
+  before_create :set_approved
 
   # This avoids a deprecation warning from Rails 5.1
   # It seems possible that a future release of devise-two-factor will
@@ -104,39 +105,6 @@ class User < ApplicationRecord
 
   attr_reader :invite_code
 
-  def pam_conflict(_)
-    # block pam login tries on traditional account
-    nil
-  end
-
-  def pam_conflict?
-    return false unless Devise.pam_authentication
-    encrypted_password.present? && pam_managed_user?
-  end
-
-  def pam_get_name
-    return account.username if account.present?
-    super
-  end
-
-  def pam_setup(_attributes)
-    acc = Account.new(username: pam_get_name)
-    acc.save!(validate: false)
-
-    self.email = "#{acc.username}@#{find_pam_suffix}" if email.nil? && find_pam_suffix
-    self.confirmed_at = Time.now.utc
-    self.admin = false
-    self.account = acc
-
-    acc.destroy! unless save
-  end
-
-  def ldap_setup(_attributes)
-    self.confirmed_at = Time.now.utc
-    self.admin = false
-    save!
-  end
-
   def confirmed?
     confirmed_at.present?
   end
@@ -145,33 +113,6 @@ class User < ApplicationRecord
     invite_id.present?
   end
 
-  def staff?
-    admin? || moderator?
-  end
-
-  def role
-    if admin?
-      'admin'
-    elsif moderator?
-      'moderator'
-    else
-      'user'
-    end
-  end
-
-  def role?(role)
-    case role
-    when 'user'
-      true
-    when 'moderator'
-      staff?
-    when 'admin'
-      admin?
-    else
-      false
-    end
-  end
-
   def disable!
     update!(disabled: true,
             last_sign_in_at: current_sign_in_at,
@@ -186,7 +127,12 @@ class User < ApplicationRecord
     new_user = !confirmed?
 
     super
-    prepare_new_user! if new_user
+
+    if new_user && approved?
+      prepare_new_user!
+    elsif new_user
+      notify_staff_about_pending_account!
+    end
   end
 
   def confirm!
@@ -194,28 +140,32 @@ class User < ApplicationRecord
 
     skip_confirmation!
     save!
-    prepare_new_user! if new_user
+
+    prepare_new_user! if new_user && approved?
   end
 
-  def update_tracked_fields!(request)
-    super
-    prepare_returning_user!
+  def pending?
+    !approved?
   end
 
-  def promote!
-    if moderator?
-      update!(moderator: false, admin: true)
-    elsif !admin?
-      update!(moderator: true)
-    end
+  def active_for_authentication?
+    super && approved?
   end
 
-  def demote!
-    if admin?
-      update!(admin: false, moderator: true)
-    elsif moderator?
-      update!(moderator: false)
-    end
+  def inactive_message
+    !approved? ? :pending : super
+  end
+
+  def approve!
+    return if approved?
+
+    update!(approved: true)
+    prepare_new_user!
+  end
+
+  def update_tracked_fields!(request)
+    super
+    prepare_returning_user!
   end
 
   def disable_two_factor!
@@ -297,43 +247,6 @@ class User < ApplicationRecord
     super
   end
 
-  def self.pam_get_user(attributes = {})
-    return nil unless attributes[:email]
-
-    resource =
-      if Devise.check_at_sign && !attributes[:email].index('@')
-        joins(:account).find_by(accounts: { username: attributes[:email] })
-      else
-        find_by(email: attributes[:email])
-      end
-
-    if resource.blank?
-      resource = new(email: attributes[:email], agreement: true)
-
-      if Devise.check_at_sign && !resource[:email].index('@')
-        resource[:email] = Rpam2.getenv(resource.find_pam_service, attributes[:email], attributes[:password], 'email', false)
-        resource[:email] = "#{attributes[:email]}@#{resource.find_pam_suffix}" unless resource[:email]
-      end
-    end
-    resource
-  end
-
-  def self.ldap_get_user(attributes = {})
-    resource = joins(:account).find_by(accounts: { username: attributes[Devise.ldap_uid.to_sym].first })
-
-    if resource.blank?
-      resource = new(email: attributes[:mail].first, agreement: true, account_attributes: { username: attributes[Devise.ldap_uid.to_sym].first })
-      resource.ldap_setup(attributes)
-    end
-
-    resource
-  end
-
-  def self.authenticate_with_pam(attributes = {})
-    return nil unless Devise.pam_authentication
-    super
-  end
-
   def show_all_media?
     setting_display_media == 'show_all'
   end
@@ -350,6 +263,10 @@ class User < ApplicationRecord
 
   private
 
+  def set_approved
+    self.approved = Setting.registrations_mode == 'open' || invited?
+  end
+
   def sanitize_languages
     return if chosen_languages.nil?
     chosen_languages.reject!(&:blank?)
@@ -367,6 +284,13 @@ class User < ApplicationRecord
     regenerate_feed! if needs_feed_update?
   end
 
+  def notify_staff_about_pending_account!
+    User.staff.includes(:account).each do |u|
+      next unless u.allows_report_emails?
+      AdminMailer.new_pending_account(u.account, self).deliver_later
+    end
+  end
+
   def regenerate_feed!
     return unless Redis.current.setnx("account:#{account_id}:regeneration", true)
     Redis.current.expire("account:#{account_id}:regeneration", 1.day.seconds)