Merge pull request 'Revert user authentication' (#148) from revert-authentication into main
Some checks failed
Check usage of free licenses / check-licenses (push) Failing after 28s
Run unit tests / unit_tests (push) Failing after 34s
Build Nginx-based docker image / build-static-assets (push) Failing after 16m17s

Reviewed-on: #148
This commit is contained in:
bustikiller 2024-11-30 09:59:42 +00:00
commit 306fa41187
20 changed files with 1 additions and 331 deletions

View File

@ -39,5 +39,3 @@ end
gem 'chroma'
gem 'solid_queue', '~> 1.0'
gem "bcrypt", "~> 3.1"

View File

@ -81,7 +81,6 @@ GEM
babel-source (>= 4.0, < 6)
execjs (~> 2.0)
base64 (0.2.0)
bcrypt (3.1.20)
benchmark (0.4.0)
bigdecimal (3.1.8)
bindex (0.8.1)
@ -364,7 +363,6 @@ PLATFORMS
DEPENDENCIES
annotaterb
bcrypt (~> 3.1)
bootsnap
chroma
csv

View File

@ -2,17 +2,5 @@
module ApplicationCable
class Connection < ActionCable::Connection::Base
identified_by :current_user
def connect
set_current_user || reject_unauthorized_connection
end
private
def set_current_user
if session = Session.find_by(id: cookies.signed[:session_id])
self.current_user = session.user
end
end
end
end

View File

@ -1,7 +1,6 @@
# Copyright (C) 2024 Manuel Bustillo
class ApplicationController < ActionController::Base
include Authentication
after_action :set_csrf_cookie
skip_before_action :verify_authenticity_token, if: :development_swagger?

View File

@ -1,57 +0,0 @@
# Copyright (C) 2024 Manuel Bustillo
module Authentication
extend ActiveSupport::Concern
included do
before_action :require_authentication
helper_method :authenticated?
end
class_methods do
def allow_unauthenticated_access(**options)
skip_before_action :require_authentication, **options
end
end
private
def authenticated?
resume_session
end
def require_authentication
resume_session || request_authentication
end
def resume_session
Current.session ||= find_session_by_cookie
end
def find_session_by_cookie
Session.find_by(id: cookies.signed[:session_id])
end
def request_authentication
session[:return_to_after_authenticating] = request.url
redirect_to new_session_path
end
def after_authentication_url
session.delete(:return_to_after_authenticating) || root_url
end
def start_new_session_for(user)
user.sessions.create!(user_agent: request.user_agent, ip_address: request.remote_ip).tap do |session|
Current.session = session
cookies.signed.permanent[:session_id] = { value: session.id, httponly: true, same_site: :lax }
end
end
def terminate_session
Current.session.destroy
cookies.delete(:session_id)
end
end

View File

@ -1,30 +0,0 @@
# Copyright (C) 2024 Manuel Bustillo
class PasswordsController < ApplicationController
allow_unauthenticated_access
before_action :set_user_by_token, only: :update
def create
if user = User.find_by(email_address: params[:email_address])
PasswordsMailer.reset(user).deliver_later
end
render json: {}, status: :created
end
def update
if @user.update(params.permit(:password, :password_confirmation))
render json: {}, status: :ok
else
render json: { errors: @user.errors.full_messages }, status: :unprocessable_entity
end
end
private
def set_user_by_token
@user = User.find_by_password_reset_token!(params[:token])
rescue ActiveSupport::MessageVerifier::InvalidSignature
render json: { errors: ['Password reset link is invalid or has expired.'] }, status: :unprocessable_entity
end
end

View File

@ -1,21 +0,0 @@
# Copyright (C) 2024 Manuel Bustillo
class SessionsController < ApplicationController
allow_unauthenticated_access only: :create
rate_limit to: 10, within: 3.minutes, only: :create,
with: -> { render json: { errors: ['Rate limit exceeded'] }, status: :too_many_requests }
def create
if user = User.authenticate_by(params.permit(:email_address, :password))
start_new_session_for user
render json: {}, status: :created
else
render json: { errors: ['Invalid email address or password'] }, status: :unauthorized
end
end
def destroy
terminate_session
render json: {}, status: :ok
end
end

View File

@ -1,8 +0,0 @@
# Copyright (C) 2024 Manuel Bustillo
class PasswordsMailer < ApplicationMailer
def reset(user)
@user = user
mail subject: "Reset your password", to: user.email_address
end
end

View File

@ -1,6 +0,0 @@
# Copyright (C) 2024 Manuel Bustillo
class Current < ActiveSupport::CurrentAttributes
attribute :session
delegate :user, to: :session, allow_nil: true
end

View File

@ -1,24 +0,0 @@
# Copyright (C) 2024 Manuel Bustillo
# == Schema Information
#
# Table name: sessions
#
# id :bigint not null, primary key
# ip_address :string
# user_agent :string
# created_at :datetime not null
# updated_at :datetime not null
# user_id :bigint not null
#
# Indexes
#
# index_sessions_on_user_id (user_id)
#
# Foreign Keys
#
# fk_rails_... (user_id => users.id)
#
class Session < ApplicationRecord
belongs_to :user
end

View File

@ -1,22 +0,0 @@
# Copyright (C) 2024 Manuel Bustillo
# == Schema Information
#
# Table name: users
#
# id :bigint not null, primary key
# email_address :string not null
# password_digest :string not null
# created_at :datetime not null
# updated_at :datetime not null
#
# Indexes
#
# index_users_on_email_address (email_address) UNIQUE
#
class User < ApplicationRecord
has_secure_password
has_many :sessions, dependent: :destroy
normalizes :email_address, with: ->(e) { e.strip.downcase }
end

View File

@ -1,6 +0,0 @@
<%# Copyright (C) 2024 Manuel Bustillo %>
<p>
You can reset your password within the next 15 minutes on
<%= link_to "this password reset page", edit_password_url(@user.password_reset_token) %>.
</p>

View File

@ -1,4 +0,0 @@
<%# Copyright (C) 2024 Manuel Bustillo %>
You can reset your password within the next 15 minutes on this password reset page:
<%= edit_password_url(@user.password_reset_token) %>

View File

@ -1,8 +1,6 @@
# Copyright (C) 2024 Manuel Bustillo
Rails.application.routes.draw do
resource :session, only: %i[create destroy]
resources :passwords, param: :token, only: %w[create update]
mount Rswag::Ui::Engine => '/api-docs'
mount Rswag::Api::Engine => '/api-docs'
resources :groups, only: :index

View File

@ -1,13 +0,0 @@
# Copyright (C) 2024 Manuel Bustillo
class CreateUsers < ActiveRecord::Migration[8.0]
def change
create_table :users do |t|
t.string :email_address, null: false
t.string :password_digest, null: false
t.timestamps
end
add_index :users, :email_address, unique: true
end
end

View File

@ -1,13 +0,0 @@
# Copyright (C) 2024 Manuel Bustillo
class CreateSessions < ActiveRecord::Migration[8.0]
def change
create_table :sessions do |t|
t.references :user, null: false, foreign_key: true
t.string :ip_address
t.string :user_agent
t.timestamps
end
end
end

20
db/schema.rb generated
View File

@ -12,7 +12,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[8.0].define(version: 2024_11_18_232618) do
ActiveRecord::Schema[8.0].define(version: 2024_11_11_063741) do
# These are extensions that must be enabled in order to support this database
enable_extension "pg_catalog.plpgsql"
@ -60,15 +60,6 @@ ActiveRecord::Schema[8.0].define(version: 2024_11_18_232618) do
t.index ["tables_arrangement_id"], name: "index_seats_on_tables_arrangement_id"
end
create_table "sessions", force: :cascade do |t|
t.bigint "user_id", null: false
t.string "ip_address"
t.string "user_agent"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["user_id"], name: "index_sessions_on_user_id"
end
create_table "solid_queue_blocked_executions", force: :cascade do |t|
t.bigint "job_id", null: false
t.string "queue_name", null: false
@ -197,19 +188,10 @@ ActiveRecord::Schema[8.0].define(version: 2024_11_18_232618) do
t.string "name", null: false
end
create_table "users", force: :cascade do |t|
t.string "email_address", null: false
t.string "password_digest", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["email_address"], name: "index_users_on_email_address", unique: true
end
add_foreign_key "groups", "groups", column: "parent_id"
add_foreign_key "guests", "groups"
add_foreign_key "seats", "guests"
add_foreign_key "seats", "tables_arrangements", on_delete: :cascade
add_foreign_key "sessions", "users"
add_foreign_key "solid_queue_blocked_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade
add_foreign_key "solid_queue_claimed_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade
add_foreign_key "solid_queue_failed_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade

View File

@ -1,42 +0,0 @@
# Copyright (C) 2024 Manuel Bustillo
require 'swagger_helper'
RSpec.describe 'passwords', type: :request do
path '/passwords' do
post('send a password (re)set email') do
tags 'Passwords'
consumes 'application/json'
produces 'application/json'
parameter name: :body, in: :body, schema: {
type: :object,
required: [:email_address],
properties: {
email_address: { type: :string, format: :email }
}
}
response_empty_201
end
end
path '/passwords/{token}' do
parameter name: 'token', in: :path, type: :string, description: 'token'
put('update password') do
tags 'Passwords'
consumes 'application/json'
produces 'application/json'
parameter name: :body, in: :body, schema: {
type: :object,
required: %i[password password_confirmation],
properties: {
password: { type: :string },
password_confirmation: { type: :string }
}
}
response_empty_200
response_422
end
end
end

View File

@ -1,31 +0,0 @@
# Copyright (C) 2024 Manuel Bustillo
require 'swagger_helper'
RSpec.describe 'sessions', type: :request do
path '/session' do
delete('delete session') do
tags 'Sessions'
produces 'application/json'
response_empty_200
end
post('create session') do
tags 'Sessions'
consumes 'application/json'
produces 'application/json'
parameter name: :body, in: :body, schema: {
type: :object,
required: %i[email_address password],
properties: {
email_address: { type: :string, format: :email },
password: { type: :string }
}
}
response_empty_201
response_401
response_429
end
end
end

View File

@ -9,22 +9,6 @@ module SwaggerResponseHelper
end
end
def response_429
response(429, 'Rate limit exceeded') do
produces 'application/json'
error_schema
xit
end
end
def response_401
response(401, 'Unauthorized') do
produces 'application/json'
error_schema
xit
end
end
def response_empty_200
response(200, 'Success') do
produces 'application/json'