Gli agenti autenticati con token Bearer possono leggere today/pipeline e annotare attività, con host MCP allineati a quelli di produzione. Co-authored-by: Cursor <cursoragent@cursor.com>
129 lines
3.6 KiB
Ruby
129 lines
3.6 KiB
Ruby
class User < ApplicationRecord
|
|
include Auditable
|
|
|
|
has_secure_password
|
|
|
|
has_many :assigned_organizations, class_name: "Organization", foreign_key: :assigned_user_id, dependent: :nullify, inverse_of: :assigned_user
|
|
has_many :assigned_opportunities, class_name: "Opportunity", foreign_key: :assigned_user_id, dependent: :nullify, inverse_of: :assigned_user
|
|
has_many :assigned_tasks, class_name: "Task", foreign_key: :assigned_user_id, dependent: :nullify, inverse_of: :assigned_user
|
|
has_many :activities, dependent: :nullify
|
|
has_many :user_projects, dependent: :destroy
|
|
has_many :projects, through: :user_projects
|
|
has_many :api_tokens, dependent: :destroy
|
|
|
|
ROLES = %w[admin user].freeze
|
|
|
|
validates :email, presence: true, uniqueness: { case_sensitive: false },
|
|
format: { with: URI::MailTo::EMAIL_REGEXP }
|
|
validates :first_name, :last_name, presence: true
|
|
validates :role, inclusion: { in: ROLES }
|
|
validates :password, length: { minimum: 8 }, if: -> { password.present? }
|
|
validate :must_keep_at_least_one_active_admin
|
|
|
|
before_validation :normalize_email
|
|
before_destroy :prevent_destroying_last_admin
|
|
|
|
scope :active, -> { where(active: true) }
|
|
scope :admins, -> { where(role: "admin") }
|
|
scope :active_admins, -> { active.admins }
|
|
|
|
def admin?
|
|
role == "admin"
|
|
end
|
|
|
|
def full_name
|
|
"#{first_name} #{last_name}"
|
|
end
|
|
|
|
def last_active_admin?
|
|
admin? && active? && self.class.active_admins.where.not(id: id).none?
|
|
end
|
|
|
|
def can_be_deactivated?
|
|
return true unless admin? && active?
|
|
|
|
!last_active_admin?
|
|
end
|
|
|
|
def can_be_destroyed?
|
|
!last_active_admin?
|
|
end
|
|
|
|
def accessible_projects
|
|
return Project.active.ordered if admin?
|
|
|
|
Project.active.ordered
|
|
.joins(:user_projects)
|
|
.where(user_projects: { user_id: id, enabled: true })
|
|
end
|
|
|
|
def can_access_project?(project)
|
|
return true if admin?
|
|
return false if project.nil?
|
|
|
|
user_projects.exists?(project_id: project.id, enabled: true)
|
|
end
|
|
|
|
def enable_project!(project)
|
|
up = user_projects.find_or_initialize_by(project: project)
|
|
up.enabled = true
|
|
up.save!
|
|
end
|
|
|
|
def disable_project!(project)
|
|
up = user_projects.find_or_initialize_by(project: project)
|
|
up.enabled = false
|
|
up.save!
|
|
end
|
|
|
|
def project_enabled?(project)
|
|
return true if admin?
|
|
|
|
user_projects.exists?(project_id: project.id, enabled: true)
|
|
end
|
|
|
|
def generate_password_reset_token!
|
|
update!(
|
|
password_reset_token: SecureRandom.urlsafe_base64(32),
|
|
password_reset_sent_at: Time.current
|
|
)
|
|
end
|
|
|
|
def password_reset_token_valid?
|
|
password_reset_token.present? &&
|
|
password_reset_sent_at.present? &&
|
|
password_reset_sent_at > 2.hours.ago
|
|
end
|
|
|
|
def clear_password_reset_token!
|
|
update!(password_reset_token: nil, password_reset_sent_at: nil)
|
|
end
|
|
|
|
private
|
|
|
|
def normalize_email
|
|
self.email = email.to_s.strip.downcase
|
|
end
|
|
|
|
def must_keep_at_least_one_active_admin
|
|
return if new_record?
|
|
|
|
was_active_admin = role_in_database == "admin" && active_in_database != false
|
|
return unless was_active_admin
|
|
|
|
deactivating = will_save_change_to_active? && !active?
|
|
demoting = will_save_change_to_role? && role != "admin"
|
|
return unless deactivating || demoting
|
|
return if self.class.active_admins.where.not(id: id).exists?
|
|
|
|
errors.add(:base, "Deve restare almeno un amministratore attivo. Crea o promuovi un altro admin prima.")
|
|
end
|
|
|
|
def prevent_destroying_last_admin
|
|
return unless last_active_admin?
|
|
|
|
errors.add(:base, "Non puoi eliminare l'unico amministratore attivo.")
|
|
throw :abort
|
|
end
|
|
end
|