diff --git a/app/controllers/concerns/authentication.rb b/app/controllers/concerns/authentication.rb index 9041f5c9..cd210cca 100644 --- a/app/controllers/concerns/authentication.rb +++ b/app/controllers/concerns/authentication.rb @@ -2,6 +2,7 @@ module Authentication extend ActiveSupport::Concern included do + before_action :set_request_details before_action :authenticate_user! end @@ -12,10 +13,9 @@ module Authentication end private - def authenticate_user! - if user = User.find_by(id: session[:user_id]) - Current.user = user + if session_record = Session.find_by_id(cookies.signed[:session_token]) + Current.session = session_record else if self_hosted_first_login? redirect_to new_registration_url @@ -25,23 +25,18 @@ module Authentication end end - def login(user) - Current.user = user - reset_session - session[:user_id] = user.id - set_last_login_at - end - - def logout - Current.user = nil - reset_session - end - - def set_last_login_at - Current.user.update(last_login_at: DateTime.now) + def create_session_for(user) + session = user.sessions.create! + cookies.signed.permanent[:session_token] = { value: session.id, httponly: true } + session end def self_hosted_first_login? Rails.application.config.app_mode.self_hosted? && User.count.zero? end + + def set_request_details + Current.user_agent = request.user_agent + Current.ip_address = request.ip + end end diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb index af628048..8c55e58d 100644 --- a/app/controllers/registrations_controller.rb +++ b/app/controllers/registrations_controller.rb @@ -17,7 +17,7 @@ class RegistrationsController < ApplicationController if @user.save Category.create_default_categories(@user.family) - login @user + @session = create_session_for(@user) flash[:notice] = t(".success") redirect_to root_path else diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index 9f546b98..b6a23195 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -1,4 +1,5 @@ class SessionsController < ApplicationController + before_action :set_session, only: :destroy skip_authentication only: %i[new create] layout "auth" @@ -8,7 +9,7 @@ class SessionsController < ApplicationController def create if user = User.authenticate_by(email: params[:email], password: params[:password]) - login user + @session = create_session_for(user) redirect_to root_path else flash.now[:alert] = t(".invalid_credentials") @@ -17,7 +18,12 @@ class SessionsController < ApplicationController end def destroy - logout + @session.destroy redirect_to root_path, notice: t(".logout_successful") end + + private + def set_session + @session = Current.user.sessions.find(params[:id]) + end end diff --git a/app/controllers/settings/profiles_controller.rb b/app/controllers/settings/profiles_controller.rb index e8f135ba..c6b93c2c 100644 --- a/app/controllers/settings/profiles_controller.rb +++ b/app/controllers/settings/profiles_controller.rb @@ -23,7 +23,7 @@ class Settings::ProfilesController < SettingsController def destroy if Current.user.deactivate - logout + Current.session.destroy redirect_to root_path, notice: t(".success") else redirect_to settings_profile_path, alert: Current.user.errors.full_messages.to_sentence @@ -31,7 +31,6 @@ class Settings::ProfilesController < SettingsController end private - def user_params params.require(:user).permit(:first_name, :last_name, :profile_image, family_attributes: [ :name, :id ]) diff --git a/app/helpers/auth_messages_helper.rb b/app/helpers/auth_messages_helper.rb deleted file mode 100644 index 116ea654..00000000 --- a/app/helpers/auth_messages_helper.rb +++ /dev/null @@ -1,6 +0,0 @@ -module AuthMessagesHelper - def auth_messages(form = nil) - render "shared/auth_messages", flash: flash, - errors: form&.object&.errors&.full_messages || [] - end -end diff --git a/app/models/current.rb b/app/models/current.rb index 316a2cf5..d12fc0a9 100644 --- a/app/models/current.rb +++ b/app/models/current.rb @@ -1,5 +1,7 @@ class Current < ActiveSupport::CurrentAttributes - attribute :user + attribute :session + attribute :user_agent, :ip_address + delegate :user, to: :session, allow_nil: true delegate :family, to: :user, allow_nil: true end diff --git a/app/models/session.rb b/app/models/session.rb new file mode 100644 index 00000000..8e94aa81 --- /dev/null +++ b/app/models/session.rb @@ -0,0 +1,8 @@ +class Session < ApplicationRecord + belongs_to :user + + before_create do + self.user_agent = Current.user_agent + self.ip_address = Current.ip_address + end +end diff --git a/app/models/user.rb b/app/models/user.rb index 7a1cca57..3add1ba4 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -2,9 +2,11 @@ class User < ApplicationRecord has_secure_password belongs_to :family + has_many :sessions, dependent: :destroy accepts_nested_attributes_for :family validates :email, presence: true, uniqueness: true + validate :ensure_valid_profile_image normalizes :email, with: ->(email) { email.strip.downcase } normalizes :first_name, :last_name, with: ->(value) { value.strip.presence } @@ -72,6 +74,14 @@ class User < ApplicationRecord end private + def ensure_valid_profile_image + return unless profile_image.attached? + + unless profile_image.content_type.in?(%w[image/jpeg image/png]) + errors.add(:profile_image, "must be a JPEG or PNG") + profile_image.purge + end + end def last_user_in_family? family.users.count == 1 diff --git a/app/views/layouts/_sidebar.html.erb b/app/views/layouts/_sidebar.html.erb index 0cec2340..7cb00841 100644 --- a/app/views/layouts/_sidebar.html.erb +++ b/app/views/layouts/_sidebar.html.erb @@ -63,7 +63,7 @@ <% end %>