Skip to content

Commit

Permalink
Add base User authentication
Browse files Browse the repository at this point in the history
  • Loading branch information
andresag4 committed Jul 8, 2024
1 parent f626c6c commit 3822f2b
Show file tree
Hide file tree
Showing 25 changed files with 338 additions and 18 deletions.
5 changes: 4 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ gem "turbo-rails"
# Authorization
gem "action_policy", "~> 0.7.0"

# Authentication
gem "bcrypt" , "~> 3.1.20"

# Other
gem "bootsnap", require: false
gem "puma", ">= 5.0"
Expand All @@ -39,7 +42,7 @@ group :development, :test do
gem "debug", platforms: %i[mri windows]
gem "dotenv"
gem "erb_lint", require: false
gem "letter_opener"
gem "letter_opener", "~> 1.10"
gem "pry-byebug"
gem "rspec-rails"
gem "rubocop-capybara", require: false
Expand Down
5 changes: 4 additions & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ GEM
ice_nine (~> 0.11.0)
thread_safe (~> 0.3, >= 0.3.1)
base64 (0.2.0)
bcrypt (3.1.20)
better_errors (2.10.1)
erubi (>= 1.0.0)
rack (>= 0.9.0)
Expand Down Expand Up @@ -471,12 +472,14 @@ GEM

PLATFORMS
arm64-darwin-21
arm64-darwin-22
arm64-darwin-23
x86_64-linux

DEPENDENCIES
action_policy (~> 0.7.0)
activerecord-enhancedsqlite3-adapter (~> 0.8.0)
bcrypt (~> 3.1.20)
better_errors
binding_of_caller
bootsnap
Expand All @@ -490,7 +493,7 @@ DEPENDENCIES
erb_lint
fuubar
importmap-rails
letter_opener
letter_opener (~> 1.10)
mission_control-jobs
propshaft
pry-byebug
Expand Down
3 changes: 3 additions & 0 deletions app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
class ApplicationController < ActionController::Base
include Authentication

before_action :authenticate_user!
end
35 changes: 35 additions & 0 deletions app/controllers/concerns/authentication.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
module Authentication
extend ActiveSupport::Concern

included do
helper_method :current_user, :user_signed_in?

def authenticate_user!
redirect_to root_path, alert: "You must be logged in to do that." unless user_signed_in?
end

def current_user
Current.user ||= authenticate_user_from_session
end

def authenticate_user_from_session
User.find_by(id: session[:user_id])
end

def user_signed_in?
current_user.present?
end

def login(user)
Current.user = user
reset_session
session[:user_id] = user.id
end

def logout
Current.user = nil
reset_session
end

end
end
5 changes: 5 additions & 0 deletions app/controllers/main_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class MainController < ApplicationController
skip_before_action :authenticate_user!
def index
end
end
43 changes: 43 additions & 0 deletions app/controllers/password_resets_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
class PasswordResetsController < ApplicationController
skip_before_action :authenticate_user!, only: [:new, :create]
before_action :set_user_by_token, only: [:edit, :update]

def new
end

def create
@user = User.find_by(email: params[:email])

if @user.present?
PasswordMailer.with(
user: @user,
token: @user.generate_token_for(:password_reset)
).password_reset.deliver_later
end

redirect_to root_path, notice: "Check your email to reset your password."
end

def edit
end

def update
if @user.update(password_params)
redirect_to new_session_path, notice: "Your password has been reset successfully. Please login."
else
render edit, status: :unprocessable_entity
end
end

private

def set_user_by_token
@user = User.find_by_token_for(:password_reset, params[:token])

redirect_to new_password_reset_path alert: "Invalid token, please try again" unless @user.present?
end

def password_params
params.require(:user).permit(:password, :password_confirmation)
end
end
24 changes: 24 additions & 0 deletions app/controllers/passwords_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
class PasswordsController < ApplicationController
before_action :authenticate_user!

def edit
end

def update
if current_user.update(password_params)
redirect_to edit_password_path, notice: "Your password has been updated successfully."
else
render :edit, status: :unprocessable_entity
end
end

private

def password_params
params.require(:user).permit(
:password,
:password_confirmation,
:password_challenge
).with_defaults(password_challenge: "")
end
end
24 changes: 24 additions & 0 deletions app/controllers/registrations_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
class RegistrationsController < ApplicationController
skip_before_action :authenticate_user!

def new
@user = User.new
end

def create
@user = User.new(registration_params)

if @user.save
login @user
redirect_to root_path
else
render :new, status: :unprocessable_entity
end
end

private

def registration_params
params.require(:user).permit(:email, :password, :password_confirmation)
end
end
23 changes: 23 additions & 0 deletions app/controllers/sessions_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
class SessionsController < ApplicationController
skip_before_action :authenticate_user!

def new
end

def create
@user = User.authenticate_by(email: params[:email], password: params[:password])

if @user
login @user
redirect_to root_path, notice: "You have signed successfully."
else
flash[:alert] = "Invalid email or password."
render :new, status: :unprocessable_entity
end
end

def destroy
logout
redirect_to root_path, notice: "You have been logged out."
end
end
5 changes: 5 additions & 0 deletions app/mailers/password_mailer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class PasswordMailer < ApplicationMailer
def password_reset
mail to: params[:user].email
end
end
3 changes: 3 additions & 0 deletions app/models/current.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class Current < ActiveSupport::CurrentAttributes
attribute :user
end
11 changes: 11 additions & 0 deletions app/models/user.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
class User < ApplicationRecord
PASSWORD_RESET_EXPIRATION = 15.minutes

has_one :profile, as: :profileable, dependent: :destroy

has_many :saved_events, dependent: :destroy
has_many :events, through: :saved_events

validates :email, presence: true, uniqueness: true
normalizes :email, with: ->(email) { email.strip.downcase }

has_secure_password

generates_token_for :password_reset, expires_in: PASSWORD_RESET_EXPIRATION do
password_salt&.last(10)
end
end
37 changes: 23 additions & 14 deletions app/views/layouts/application.html.erb
Original file line number Diff line number Diff line change
@@ -1,19 +1,28 @@
<!DOCTYPE html>
<html>
<head>
<title>RailsWorld</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= stylesheet_link_tag "tailwind", "inter-font", "data-turbo-track": "reload" %>
<head>
<title>RailsWorld</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%#= stylesheet_link_tag "tailwind", "inter-font", "data-turbo-track": "reload" %>

<%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
<%= javascript_importmap_tags %>
</head>
<%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
<%= javascript_importmap_tags %>
</head>

<body>
<main class="container mx-auto mt-28 px-5 flex">
<%= yield %>
</main>
</body>
<body>
<main class="container mx-auto mt-28 px-5 flex">
<div><%= notice %></div>
<div><%= alert %></div>
<% if user_signed_in? %>
<%= link_to "Edit Password", edit_password_path %>
<%= button_to "Log out", session_path, method: :delete %>
<% else %>
<%= link_to "Sign Up", new_registration_path %>
<%= link_to "Log in", new_session_path %>
<% end %>
<%= yield %>
</main>
</body>
</html>
1 change: 1 addition & 0 deletions app/views/main/index.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<h1>Homepage</h1>
1 change: 1 addition & 0 deletions app/views/password_mailer/password_reset.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<%= link_to "Reset your password", edit_password_reset_url(token: params[:token]) %>
23 changes: 23 additions & 0 deletions app/views/password_resets/edit.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<h1>Reset Your Password</h1>

<%= form_with model: @user, url: password_reset_path(token: params[:token]) do |form| %>
<% if form.object.errors.any? %>
<% form.object.errors.full_messages.each do |message| %>
<div> <%= message %></div>
<% end %>
<% end %>

<div>
<%= form.label :password %>
<%= form.password_field :password %>
</div>

<div>
<%= form.label :password_confirmation %>
<%= form.password_field :password_confirmation %>
</div>

<div>
<%= form.submit "Reset Your Password" %>
</div>
<% end %>
12 changes: 12 additions & 0 deletions app/views/password_resets/new.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<h1>Reset Your Password</h1>

<%= form_with model: @user, url: password_reset_path do |form| %>
<div>
<%= form.label :email %>
<%= form.email_field :email %>
</div>

<div>
<%= form.submit "Reset password" %>
</div>
<% end %>
28 changes: 28 additions & 0 deletions app/views/passwords/edit.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<h1>Update Password</h1>

<%= form_with model: current_user, url: password_path do |form| %>
<% if form.object.errors.any? %>
<% form.object.errors.full_messages.each do |message| %>
<div><%= message %></div>
<% end %>
<% end %>

<div>
<%= form.label :password_challenge, "Current Password" %>
<%= form.password_field :password_challenge %>
</div>

<div>
<%= form.label :password, "New Password" %>
<%= form.password_field :password %>
</div>

<div>
<%= form.label :password_confirmation %>
<%= form.password_field :password_confirmation %>
</div>

<div>
<%= form.submit "Update Password" %>
</div>
<% end %>
28 changes: 28 additions & 0 deletions app/views/registrations/new.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<h1>Sign Up</h1>

<%= form_with model: @user, url: registration_path do |form| %>
<% if form.object.errors.any? %>
<% form.object.errors.full_messages.each do |message| %>
<div><%= message %></div>
<% end %>
<% end %>

<div>
<%= form.label :email %>
<%= form.email_field :email %>
</div>

<div>
<%= form.label :password %>
<%= form.password_field :password %>
</div>

<div>
<%= form.label :password_confirmation %>
<%= form.password_field :password_confirmation %>
</div>

<div>
<%= form.submit "Sign Up" %>
</div>
<% end %>
Loading

0 comments on commit 3822f2b

Please sign in to comment.