From 9e33174604952490136a6f8cce2c9bd3ca03a26c Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Thu, 14 Mar 2019 02:13:42 +0100 Subject: [PATCH] Refactor User model, extract PamAuthenticable, LdapAuthenticable (#10217) --- app/models/concerns/ldap_authenticable.rb | 25 +++++ app/models/concerns/omniauthable.rb | 2 + app/models/concerns/pam_authenticable.rb | 68 ++++++++++++ app/models/concerns/user_roles.rb | 54 ++++++++++ app/models/user.rb | 126 ++-------------------- 5 files changed, 155 insertions(+), 120 deletions(-) create mode 100644 app/models/concerns/ldap_authenticable.rb create mode 100644 app/models/concerns/pam_authenticable.rb create mode 100644 app/models/concerns/user_roles.rb diff --git a/app/models/concerns/ldap_authenticable.rb b/app/models/concerns/ldap_authenticable.rb new file mode 100644 index 0000000000..e1b5e3832b --- /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 4dd2e9383c..1b28b8162e 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 0000000000..2f651c1a35 --- /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 0000000000..58dffdc46f --- /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/user.rb b/app/models/user.rb index 83816def57..a9d6adf706 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -41,7 +41,7 @@ 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 +61,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 +79,6 @@ 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 :confirmed, -> { where.not(confirmed_at: nil) } scope :enabled, -> { where(disabled: false) } scope :inactive, -> { where(arel_table[:current_sign_in_at].lt(ACTIVE_DURATION.ago)) } @@ -104,39 +101,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 +109,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,6 +123,7 @@ class User < ApplicationRecord new_user = !confirmed? super + prepare_new_user! if new_user end @@ -194,6 +132,7 @@ class User < ApplicationRecord skip_confirmation! save! + prepare_new_user! if new_user end @@ -202,22 +141,6 @@ class User < ApplicationRecord prepare_returning_user! 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 - def disable_two_factor! self.otp_required_for_login = false otp_backup_codes&.clear @@ -297,43 +220,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