Skip to content

Commit

Permalink
Implement password restoration 🐙
Browse files Browse the repository at this point in the history
andrewr224 committed May 4, 2018
1 parent 712eb8e commit fa53410
Showing 19 changed files with 318 additions and 19 deletions.
33 changes: 33 additions & 0 deletions app/controllers/v1/padlock/restoration_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
module V1
module Padlock
class RestorationController < V1::Padlock::PadlockController
skip_after_action :insert_token

def create
return head :not_found unless user

V1::Padlock::Restoration::Create.call(user: user)

head :ok
end

def update
return head :not_found unless user

user.update(user_params)

@token = Token.find_by(key: params[:token], key_type: :restoration)

::Padlock::TokenDestroyer.call(token: @token)

head :ok
end

private

def user_params
params.require(:user).permit(:password, :password_confirmation)
end
end
end
end
26 changes: 26 additions & 0 deletions app/mailers/v1/padlock/restoration_mailer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
module V1
module Padlock
class RestorationMailer < ApplicationMailer
def restoration_email(params = {})
@user_id = params[:user_id]
@token_key = params[:token_key]

return unless user && @token_key

mail(to: email, subject: t('padlock.mailers.restoration_mailer.restore_password'))
end

private

attr_reader :user_id

def user
@user ||= User.find_by(id: user_id)
end

def email
user.identities.first.uid
end
end
end
end
6 changes: 3 additions & 3 deletions app/mailers/v1/padlock/verification_mailer.rb
Original file line number Diff line number Diff line change
@@ -2,10 +2,10 @@ module V1
module Padlock
class VerificationMailer < ApplicationMailer
def verification_email(params = {})
@user_id = params[:user_id]
@token = params[:token]
@user_id = params[:user_id]
@token_key = params[:token_key]

return unless user && @token
return unless user && @token_key

mail(to: email, subject: t('padlock.mailers.verification_mailer.verify_email'))
end
34 changes: 34 additions & 0 deletions app/operations/v1/padlock/restoration/create.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
module V1
module Padlock
module Restoration
class Create < ::Callable
delegate :restoration_email, to: V1::Padlock::RestorationMailer

def initialize(params = {})
@user = params[:user]
end

def call
return if user&.new_record?

send_email
end

private

attr_reader :user

def token
@token ||= ::Padlock::TokenGenerator.call(
user_id: user.id,
key_type: :restoration
)
end

def send_email
restoration_email(user_id: user.id, token_key: token.key).deliver
end
end
end
end
end
2 changes: 1 addition & 1 deletion app/operations/v1/padlock/verification/create.rb
Original file line number Diff line number Diff line change
@@ -26,7 +26,7 @@ def token
end

def send_email
verification_email(user_id: user.id, token: token).deliver
verification_email(user_id: user.id, token_key: token.key).deliver
end
end
end
2 changes: 1 addition & 1 deletion app/services/padlock/token_generator.rb
Original file line number Diff line number Diff line change
@@ -27,7 +27,7 @@ def expired_at
@expired_at ||= case key_type
when :auth
Time.zone.now + 1.month
when :verification
else
Time.zone.now + 1.hour
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<div>
<p><%= t('padlock.emails.restoration_email.instruction') %></p>
<p>
<%= link_to t('padlock.emails.restoration_email.link_name'), verification_url(token: @token_key) %>
</p>
<p><%= t('padlock.emails.restoration_email.warning') %></p>
</div>
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<div>
<p><%= t('padlock.emails.verification_email.instruction') %></p>
<p>
<%= link_to t('padlock.emails.verification_email.link_name'), verification_url(token: @token) %>
<%= link_to t('padlock.emails.verification_email.link_name'), verification_url(token: @token_key) %>
</p>
<p><%= t('padlock.emails.verification_email.warning') %></p>
</div>
6 changes: 6 additions & 0 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
@@ -5,9 +5,15 @@ en:
mailers:
verification_mailer:
verify_email: Verify your email address
restoration_mailer:
restore_password: Restore your password

emails:
verification_email:
instruction: Click on the link below to verify your email address.
link_name: Verify my email
warning: If you did not request your email address to be verified, ignore this email.
restoration_email:
instruction: Click on the link below to restore your password.
link_name: Restore my password
warning: If you did not request your password to be restored, ignore this email.
1 change: 1 addition & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@
specify :registration
specify :session
specify :verification
specify :restoration
end
end

11 changes: 11 additions & 0 deletions config/routes/v1/padlock/restoration.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module Routes
module V1
module Padlock
module Restoration
def call
resource :restoration, only: %i[create update], controller: :restoration
end
end
end
end
end
4 changes: 4 additions & 0 deletions spec/factories/token.rb
Original file line number Diff line number Diff line change
@@ -9,5 +9,9 @@
trait :verification do
key_type :verification
end

trait :restoration do
key_type :restoration
end
end
end
4 changes: 4 additions & 0 deletions spec/factories/user.rb
Original file line number Diff line number Diff line change
@@ -21,6 +21,10 @@
after(:create) { |user| user.tokens << create(:token, :verification) }
end

trait :restoration_token do
after(:create) { |user| user.tokens << create(:token, :restoration) }
end

trait :verified do
verified_at { Faker::Time.between(1.month.ago, Date.today) }
end
34 changes: 34 additions & 0 deletions spec/mailers/padlock/restoration_mailer_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
require 'rails_helper'

describe V1::Padlock::RestorationMailer, type: :mailer do
let(:mail) { described_class.restoration_email(params) }
let(:params) { { user_id: user.id, token_key: token.key } }
let(:user) { create(:user, :email, :restoration_token) }
let(:token) { user.tokens.first }

context 'whith token' do
it 'renders the subject' do
expect(mail.subject).to eq('Restore your password')
end

it 'renders the receiver email' do
expect(mail.to).to eq([user.identities.first.uid])
end

it 'renders the body with token' do
expect(mail.body.encoded).to match('Restore my password')
end

it 'contains verification URL' do
expect { restoration_url }.not_to raise_error
end
end

context 'without a token' do
let(:params) { { user_id: user.id } }

it "doesn't render the body" do
expect(mail.body).to be_empty
end
end
end
12 changes: 6 additions & 6 deletions spec/mailers/padlock/verification_mailer_spec.rb
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
require 'rails_helper'

describe V1::Padlock::VerificationMailer, type: :mailer do
let(:mail) { V1::Padlock::VerificationMailer.verification_email(params) }
let(:params) { { user_id: user.id, token: token } }
let(:token) { SecureRandom.base64(8) }
let(:user) { create(:user, :email) }
let(:mail) { described_class.verification_email(params) }
let(:params) { { user_id: user.id, token_key: token.key } }
let(:token) { user.tokens.first }
let(:user) { create(:user, :email, :verification_token) }

context 'whith token' do
it 'renders the subject' do
expect(mail.subject).to eq('Verify your email address')
end

it 'renders the receiver email' do
expect(mail.to).to eq([user.identities.first.uid])
expect(mail.to).to eq([user.identities.first.uid])
end

it 'renders the body with token' do
@@ -25,7 +25,7 @@
end

context 'without a token' do
let(:token) { nil }
let(:params) { { user_id: user.id } }

it "doesn't render the body" do
expect(mail.body).to be_empty
29 changes: 29 additions & 0 deletions spec/operations/v1/padlock/restoration/create_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
require 'rails_helper'

describe V1::Padlock::Restoration::Create do
subject { described_class.call(params) }

let(:params) { { user: user } }
let(:user) { create(:user, :email) }

context 'email' do
it 'calls Token Generator' do
expect(::Padlock::TokenGenerator)
.to receive(:call)
.and_return(create(:token))

subject
end

it 'calls Restoration Mailer' do
expect(V1::Padlock::RestorationMailer)
.to receive(:restoration_email)
.and_return(double(V1::Padlock::RestorationMailer, deliver: true))

subject
end
end

context 'phone' do
end
end
6 changes: 4 additions & 2 deletions spec/operations/v1/padlock/verification/create_spec.rb
Original file line number Diff line number Diff line change
@@ -4,11 +4,13 @@
subject { described_class.call(params) }

let(:params) { { user: user } }
let(:user) { create(:user) }
let(:user) { create(:user, :email) }

context 'email' do
it 'calls Token Generator' do
expect(::Padlock::TokenGenerator).to receive(:call)
expect(::Padlock::TokenGenerator)
.to receive(:call)
.and_return(create(:token))

subject
end
113 changes: 113 additions & 0 deletions spec/requests/v1/padlock/restoration_request_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
require 'rails_helper'

describe V1::Padlock::RestorationController, type: :request do
describe '#create' do
let(:request) { post restoration_path }

context 'email' do
let!(:user) { create(:user, :email) }

context 'with existing user' do
before do
allow_any_instance_of(::Padlock::UserByToken)
.to receive(:call)
.and_return(user)

expect(V1::Padlock::RestorationMailer)
.to receive(:restoration_email)
.and_return(double(V1::Padlock::RestorationMailer, deliver: true))
end

context 'response' do
before { request }

include_examples 'success status'
end

context 'behavior' do
it 'creates new validation token' do
expect{ request }.to change(Token, :count).by(1)
end
end
end

context 'without a user' do
before do
expect(V1::Padlock::VerificationMailer)
.not_to receive(:verification_email)
end

context 'response' do
before { request }

it "return status 'not found'" do
expect(response).to have_http_status(:not_found)
end
end

context 'behavior' do
it "doesn't create validation token" do
expect{ request }.not_to change(Token, :count)
end
end
end
end

context 'phone' do
end
end

describe 'update' do
let(:request) { patch restoration_path params: params }
let(:params) { { user: user_params, token: token.key } }
let(:user_params) { build(:user_params) }

context 'email' do
let!(:user) { create(:user, :email, :restoration_token) }
let(:token) { user.tokens.first }

context 'with valid token' do
before do
allow_any_instance_of(::Padlock::UserByToken)
.to receive(:call)
.and_return(user)

# allow_any_instance_of(V1::Padlock::RestorationController)
# .to receive(:params)
# .and_return({ token: token.key })
end

context 'response' do
before { request }

include_examples 'success status'
end

context 'behavior' do
it "changes 'password' field" do
expect{ request }.to change(user, :password)
end

it 'destroys restoration token' do
expect{ request }.to change(Token, :count).by(-1)
end
end

context 'when password confirmation does not match' do

end
end

context 'with invalid token' do
before { request }

it "return status 'not found'" do
expect(response).to have_http_status(:not_found)
end
end
end

context 'phone' do
end
end
end
5 changes: 0 additions & 5 deletions spec/requests/v1/padlock/restorations_request_spec.rb

This file was deleted.

0 comments on commit fa53410

Please sign in to comment.