Skip to content

Commit

Permalink
Merge pull request #8 from felipejoglar/felipejoglar/login
Browse files Browse the repository at this point in the history
Add login
  • Loading branch information
felipejoglar authored Feb 6, 2024
2 parents 9c94b15 + 609e3bd commit b6ff60b
Show file tree
Hide file tree
Showing 26 changed files with 431 additions and 35 deletions.
27 changes: 27 additions & 0 deletions .idea/dataSources.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 12 additions & 4 deletions app/assets/stylesheets/application.tailwind.css
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,19 @@

/* Forms */
.form-container {
@apply bg-light-subtle/50 dark:bg-dark-subtle/50 rounded-lg my-4 px-8 pt-2
@apply bg-light-subtle/50 dark:bg-dark-subtle/50 rounded-lg my-4 px-8 py-4
}

.error-container {
@apply w-full p-2 mb-2 bg-error/10 border border-error rounded-md text-center text-error
.message {
@apply w-full p-2 mb-2 border rounded-md text-center
}

.alert {
@apply bg-error/10 border-error text-error
}

.notice {
@apply bg-tertiary/10 border-tertiary text-dark dark:text-tertiary
}

.label {
Expand All @@ -56,7 +64,7 @@
}

.input-error {
@apply placeholder:text-error border-error focus:shadow-error/50 focus:border-error
@apply placeholder:text-error/80 border-error focus:shadow-error/50 focus:border-error
}
}

Expand Down
1 change: 1 addition & 0 deletions app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
class ApplicationController < ActionController::Base
include Authentication
end
22 changes: 22 additions & 0 deletions app/controllers/home_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# frozen_string_literal: true

# Copyright 2024 Felipe Joglar
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

class HomeController < ApplicationController
before_action :authenticate_user!

def index
end
end
51 changes: 51 additions & 0 deletions app/controllers/password_reset_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# frozen_string_literal: true

# Copyright 2024 Felipe Joglar
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

class PasswordResetController < ApplicationController
before_action :user_by_token, only: [:edit, :update]

def new
end

def create
User.find_by(email: params[:user][:email])&.password_reset_requested

redirect_to login_path, notice: t("auth.password_reset.message.confirmation")
end

def edit
end

def update
if @user.update(password_params)
login @user
else
render :edit, status: :unprocessable_entity
end
end

private

def password_params
params.require(:user).permit(:password, :password_confirmation)
end

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

redirect_to forgot_password_path, notice: t("auth.password_reset.message.expired") unless @user.present?
end
end
37 changes: 37 additions & 0 deletions app/controllers/sessions_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Copyright 2024 Felipe Joglar
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

class SessionsController < ApplicationController
before_action :redirect_if_authenticated, only: [:create, :new]

def new
end

def create
email = params[:user][:email].strip.downcase
password = params[:user][:password]

if (user = User.authenticate_by(email: email, password: password))
login user
else
flash.now[:alert] = t("auth.log_in.error_message")
render :new, status: :unprocessable_entity
end
end

def destroy
logout
redirect_to root_path
end
end
2 changes: 1 addition & 1 deletion app/controllers/users_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class UsersController < ApplicationController
def create
@user = User.new(user_params)
if @user.save
redirect_to root_path, notice: "You have signed up successfully!"
login @user
else
render :new, status: :unprocessable_entity
end
Expand Down
2 changes: 2 additions & 0 deletions app/helpers/sessions_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
module SessionsHelper
end
20 changes: 20 additions & 0 deletions app/mailers/user_mailer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Copyright 2024 Felipe Joglar
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

class UserMailer < ApplicationMailer

def password_reset
mail to: params[:user].email, subject: "Reset your password"
end
end
55 changes: 55 additions & 0 deletions app/models/concerns/authentication.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# frozen_string_literal: true

# Copyright 2024 Felipe Joglar
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

module Authentication
extend ActiveSupport::Concern

included do
before_action :current_user

helper_method :current_user
helper_method :user_signed_in?
end

def login(user)
reset_session
session[:current_user_id] = user.id

redirect_to home_path(user.id)
end

def logout
reset_session
end

def redirect_if_authenticated
redirect_to home_path(current_user.id) if user_signed_in?
end

def authenticate_user!
redirect_to root_path unless user_signed_in?
end

private

def current_user
Current.user ||= session[:current_user_id] && User.find_by(id: session[:current_user_id])
end

def user_signed_in?
Current.user.present?
end
end
19 changes: 19 additions & 0 deletions app/models/current.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# frozen_string_literal: true

# Copyright 2024 Felipe Joglar
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

class Current < ActiveSupport::CurrentAttributes
attribute :user
end
15 changes: 15 additions & 0 deletions app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,19 @@ class User < ApplicationRecord

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


def password_reset_requested
UserMailer.with(user: self, token: generate_token_for(:password_reset))
.password_reset
.deliver_later
end

private

RESET_PASSWORD_TOKEN_EXPIRATION = 15.minutes

generates_token_for :password_reset, expires_in: RESET_PASSWORD_TOKEN_EXPIRATION do
password_salt&.last(12)
end
end
2 changes: 2 additions & 0 deletions app/views/home/index.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<h1>Welcome <%= current_user.name %></h1>
<%= button_to "Logout", logout_path, method: :delete %>
6 changes: 3 additions & 3 deletions app/views/landing/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
<h1 class="text-primary-gradient font-bold"><%= t("app_name") %></h1>
</div>
<div>
<a class="btn-primary" href="#">
<%= t("landing.log_in") %>
<a class="btn-primary" href="<%= login_path %>">
<%= t("auth.login") %>
</a>
<a class="btn-primary" href="<%= signup_path %>">
<%= t("common.sign_up") %>
<%= t("auth.signup") %>
</a>
</div>
</div>
22 changes: 22 additions & 0 deletions app/views/password_reset/edit.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<div class="max-w-md mx-auto">
<div class="text-center">
<img class="max-w-24 mx-auto mb-4 mt-16" src="<%= asset_path("logo-coloured.svg") %>" alt="Taskodoro logo">
<h2 class="my-4"><%= t("auth.password_reset.title") %></h2>
</div>
<div class="form-container">
<%= form_with model: @user, url: password_path(token: params[:token]) do |form| %>
<%= render "shared/form_flash", object: form.object %>
<div>
<%= form.label :password, t("auth.password_reset.password_label"), class: "label" %>
<%= form.password_field :password, required: true, autofocus: true, class: "input" %>
</div>
<div>
<%= form.label :password_confirmation, t("auth.password_reset.confirmation_label"), class: "label" %>
<%= form.password_field :password_confirmation, required: true, class: "input" %>
</div>
<div class="text-center">
<%= form.submit t("auth.password_reset.button"), class: "btn-primary mx-0 w-full" %>
</div>
<% end %>
</div>
</div>
18 changes: 18 additions & 0 deletions app/views/password_reset/new.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<div class="max-w-md mx-auto">
<div class="text-center">
<img class="max-w-24 mx-auto mb-4 mt-16" src="<%= asset_path("logo-coloured.svg") %>" alt="Taskodoro logo">
<h2 class="my-4"><%= t("auth.forgot_password.title") %></h2>
</div>
<div class="form-container">
<p class="m-2"><%= t("auth.forgot_password.headline") %></p>
<%= form_with url: password_path, scope: :user do |form| %>
<%= render "shared/form_flash", object: form.object %>
<div>
<%= form.email_field :email, placeholder: t("auth.forgot_password.placeholder"), required: true, autofocus: true, class: "input" %>
</div>
<div class="text-center">
<%= form.submit t("auth.forgot_password.button"), class: "btn-primary mx-0 w-full" %>
</div>
<% end %>
</div>
</div>
25 changes: 25 additions & 0 deletions app/views/sessions/new.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<div class="max-w-md mx-auto">
<div class="text-center">
<img class="max-w-24 mx-auto mb-4 mt-16" src="<%= asset_path("logo-coloured.svg") %>" alt="Taskodoro logo">
<h2 class="my-4"><%= t("auth.log_in.title") %></h2>
</div>
<div class="form-container">
<%= form_with url: login_path, scope: :user do |form| %>
<%= render "shared/form_flash", object: form.object %>
<div>
<%= form.label :email, class: "label" %>
<%= form.email_field :email, placeholder: t("auth.placeholder.email"), required: true, autofocus: true, class: "input" %>
</div>
<div>
<%= form.label :password, class: "label" %>
<%= form.password_field :password, placeholder: t("auth.placeholder.password"), required: true, class: "input" %>
</div>
<div class="text-center">
<%= form.submit t("auth.login"), class: "btn-primary mx-0 w-full" %>
</div>
<% end %>
</div>
<div class="text-center">
<%= link_to t("auth.log_in.forgot_password"), forgot_password_path, class: "text-sm" %>
</div>
</div>
Loading

0 comments on commit b6ff60b

Please sign in to comment.