Skip to content

Commit

Permalink
adds bullet gem, passwordless, with tailwindish forms and alerts + a …
Browse files Browse the repository at this point in the history
…spec for auth
  • Loading branch information
fermion committed Nov 20, 2023
1 parent 7866ec8 commit 62ceb78
Show file tree
Hide file tree
Showing 26 changed files with 402 additions and 11 deletions.
9 changes: 5 additions & 4 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ gem 'dotenv-rails', groups: [:development, :test]
group :development, :test do
# See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem
gem "debug", platforms: %i[ mri windows ]
gem "bullet"
gem "faker"
end

group :development do
Expand All @@ -61,9 +63,7 @@ group :development do
# Add speed badges [https://github.com/MiniProfiler/rack-mini-profiler]
# gem "rack-mini-profiler"

# Speed up commands on slow machines / big apps [https://github.com/rails/spring]
# gem "spring"

gem "letter_opener"
end

group :test do
Expand All @@ -73,5 +73,6 @@ group :test do

gem "rspec-rails"
gem "factory_bot_rails"
gem "faker"
end

gem "passwordless", "~> 1.1"
15 changes: 15 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,15 @@ GEM
addressable (2.8.5)
public_suffix (>= 2.0.2, < 6.0)
base64 (0.2.0)
bcrypt (3.1.20)
bigdecimal (3.1.4)
bindex (0.8.1)
bootsnap (1.17.0)
msgpack (~> 1.2)
builder (3.2.4)
bullet (7.1.4)
activesupport (>= 3.0.0)
uniform_notifier (~> 1.11)
capybara (3.39.2)
addressable
matrix
Expand Down Expand Up @@ -129,6 +133,10 @@ GEM
jbuilder (2.11.5)
actionview (>= 5.0.0)
activesupport (>= 5.0.0)
launchy (2.5.2)
addressable (~> 2.8)
letter_opener (1.8.1)
launchy (>= 2.2, < 3)
loofah (2.22.0)
crass (~> 1.0.2)
nokogiri (>= 1.12.0)
Expand Down Expand Up @@ -159,6 +167,9 @@ GEM
racc (~> 1.4)
nokogiri (1.15.5-x86_64-linux)
racc (~> 1.4)
passwordless (1.1.1)
bcrypt (>= 3.1.11)
rails (>= 5.1.4)
pg (1.5.4)
psych (5.1.1.1)
stringio
Expand Down Expand Up @@ -257,6 +268,7 @@ GEM
railties (>= 6.0.0)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
uniform_notifier (1.16.0)
web-console (4.2.1)
actionview (>= 6.0.0)
activemodel (>= 6.0.0)
Expand All @@ -279,13 +291,16 @@ PLATFORMS

DEPENDENCIES
bootsnap
bullet
capybara
debug
dotenv-rails
factory_bot_rails
faker
importmap-rails
jbuilder
letter_opener
passwordless (~> 1.1)
pg (~> 1.1)
puma (>= 5.0)
rails (~> 7.1.1)
Expand Down
15 changes: 15 additions & 0 deletions app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
@@ -1,2 +1,17 @@
class ApplicationController < ActionController::Base
include Passwordless::ControllerHelpers

helper_method :current_user

private

def current_user
@current_user ||= authenticate_by_session(User)
end

def require_user!
return if current_user
save_passwordless_redirect_location!(User)
redirect_to auth_sign_in_url, flash: { notice: 'Please sign in.' }
end
end
6 changes: 6 additions & 0 deletions app/controllers/dashboard_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
class DashboardController < ApplicationController
before_action :require_user!

def show
end
end
2 changes: 2 additions & 0 deletions app/helpers/dashboard_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
module DashboardHelper
end
8 changes: 8 additions & 0 deletions app/models/user.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
class User < ApplicationRecord
validates :email,
presence: true,
uniqueness: { case_sensitive: false },
format: { with: URI::MailTo::EMAIL_REGEXP }

passwordless_with :email
end
4 changes: 4 additions & 0 deletions app/views/dashboard/show.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<div>
<h1 class="font-bold text-4xl">Dashboard#show</h1>
<p>Find me in app/views/dashboard/show.html.erb</p>
</div>
1 change: 1 addition & 0 deletions app/views/passwordless/mailer/sign_in.text.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<%= t("passwordless.mailer.sign_in.body", token: @token, magic_link: @magic_link) %>
38 changes: 38 additions & 0 deletions app/views/passwordless/sessions/new.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<div class="container mx-auto">
<div class="flex min-h-full flex-col justify-center py-12 sm:px-6 lg:px-8">
<div class="sm:mx-auto sm:w-full sm:max-w-md">
<img class="mx-auto h-10 w-auto" src="https://tailwindui.com/img/logos/mark.svg?color=indigo&shade=600" alt="Your Company">
<h2 class="mt-6 text-center text-2xl font-bold leading-9 tracking-tight text-gray-900">Sign in to your account</h2>
</div>

<div class="mt-10 sm:mx-auto sm:w-full sm:max-w-[480px]">
<div class="bg-white px-6 py-12 shadow sm:rounded-lg sm:px-12">
<%= render("shared/flash") %>
<%= form_with(model: @session, url: url_for(action: 'new'), html: { class: "space-y-6" }, data: { turbo: 'false' }) do |f| %>
<% email_field_name = :"passwordless[#{email_field}]" %>
<div>
<%= f.label email_field_name,
t("passwordless.sessions.new.email.label"),
class: "block text-sm font-medium leading-6 text-gray-900",
for: "passwordless_#{email_field}" %>
<div class="mt-2">
<%= email_field_tag email_field_name,
params.fetch(email_field_name, nil),
required: true,
autofocus: true,
placeholder: t("passwordless.sessions.new.email.placeholder"),
class: "block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" %>
</div>
</div>

<div>
<%= f.submit t("passwordless.sessions.new.submit"), class: "flex w-full justify-center rounded-md bg-indigo-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600" %>
</div>
<% end %>
<p class="text-center text-sm leading-6 text-gray-500 pt-6">
StaffPlan does not use passwords. You will receive an email with a link to sign in.
</p>
</div>
</div>
</div>
</div>
30 changes: 30 additions & 0 deletions app/views/passwordless/sessions/show.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<div class="container mx-auto">
<div class="flex min-h-full flex-col justify-center py-12 sm:px-6 lg:px-8">
<div class="sm:mx-auto sm:w-full sm:max-w-md">
<img class="mx-auto h-10 w-auto" src="https://tailwindui.com/img/logos/mark.svg?color=indigo&shade=600" alt="Your Company">
<h2 class="mt-6 text-center text-2xl font-bold leading-9 tracking-tight text-gray-900">Sign in to your account</h2>
</div>

<div class="mt-10 sm:mx-auto sm:w-full sm:max-w-[480px]">
<div class="bg-white px-6 py-12 shadow sm:rounded-lg sm:px-12">
<%= render("shared/flash") %>
<%= form_with(model: @session, url: url_for(action: 'update'), scope: 'passwordless', method: 'patch', data: { turbo: false }) do |f| %>

<div>
<%= f.label :token, autocomplete: "off", class: "block text-sm font-medium leading-6 text-gray-900" %>
<div class="mt-2">
<%= f.text_field :token, class: "block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" %>
</div>
</div>

<div>
<%= f.submit t(".confirm"), class: "mt-2 flex w-full justify-center rounded-md bg-indigo-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600" %>
</div>
<% end %>
<p class="text-center text-sm leading-6 text-gray-500 pt-6">
StaffPlan does not use passwords. You will receive an email with a link to sign in.
</p>
</div>
</div>
</div>
</div>
30 changes: 30 additions & 0 deletions app/views/shared/_flash.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<% if flash[:error].present? %>
<div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative" role="alert">
<strong class="font-bold">Holy smokes!</strong>
<span class="block sm:inline"><%= flash[:error] %></span>
<span class="absolute top-0 bottom-0 right-0 px-4 py-3">
</span>
</div>
<% end %>

<% if flash[:notice] %>
<div class="flex items-center bg-blue-500 text-white text-sm font-bold px-4 py-3" role="alert">
<svg class="fill-current w-4 h-4 mr-2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M12.432 0c1.34 0 2.01.912 2.01 1.957 0 1.305-1.164 2.512-2.679 2.512-1.269 0-2.009-.75-1.974-1.99C9.789 1.436 10.67 0 12.432 0zM8.309 20c-1.058 0-1.833-.652-1.093-3.524l1.214-5.092c.211-.814.246-1.141 0-1.141-.317 0-1.689.562-2.502 1.117l-.528-.88c2.572-2.186 5.531-3.467 6.801-3.467 1.057 0 1.233 1.273.705 3.23l-1.391 5.352c-.246.945-.141 1.271.106 1.271.317 0 1.357-.392 2.379-1.207l.6.814C12.098 19.02 9.365 20 8.309 20z"/></svg>
<p><%= flash[:notice] %></p>
</div>
<% end %>

<% if flash[:info].present? %>

<% end %>

<% if flash[:success].present? %>
<div class="bg-teal-100 border-t-4 border-teal-500 rounded-b text-teal-900 px-4 py-3 shadow-md" role="alert">
<div class="flex">
<div class="py-1"><svg class="fill-current h-6 w-6 text-teal-500 mr-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M2.93 17.07A10 10 0 1 1 17.07 2.93 10 10 0 0 1 2.93 17.07zm12.73-1.41A8 8 0 1 0 4.34 4.34a8 8 0 0 0 11.32 11.32zM9 11V9h2v6H9v-4zm0-6h2v2H9V5z"/></svg></div>
<div>
<p class="text-sm"><%= flash[:success] %></p>
</div>
</div>
</div>
<% end %>
15 changes: 14 additions & 1 deletion config/environments/development.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
require "active_support/core_ext/integer/time"

Rails.application.configure do
config.after_initialize do
Bullet.enable = true
Bullet.alert = true
Bullet.bullet_logger = true
Bullet.console = true
Bullet.rails_logger = true
Bullet.add_footer = true
end

# Settings specified here will take precedence over those in config/application.rb.

# In the development environment your application's code is reloaded any time
Expand Down Expand Up @@ -38,7 +47,8 @@

# Don't care if the mailer can't send.
config.action_mailer.raise_delivery_errors = false

config.action_mailer.delivery_method = :letter_opener
config.action_mailer.perform_deliveries = true
config.action_mailer.perform_caching = false

# Print deprecation notices to the Rails logger.
Expand Down Expand Up @@ -73,4 +83,7 @@

# Raise error when a before_action's only/except options reference missing actions
config.action_controller.raise_on_missing_callback_actions = true

Rails.application.routes.default_url_options[:host] = 'localhost'
Rails.application.routes.default_url_options[:port] = 3000
end
3 changes: 3 additions & 0 deletions config/environments/production.rb
Original file line number Diff line number Diff line change
Expand Up @@ -94,4 +94,7 @@
# ]
# Skip DNS rebinding protection for the default health check endpoint.
# config.host_authorization = { exclude: ->(request) { request.path == "/up" } }

Rails.application.routes.default_url_options[:host] = 'staffplan.com'
Rails.application.routes.default_url_options[:protocol] = 'https'
end
9 changes: 9 additions & 0 deletions config/environments/test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@
# and recreated between test runs. Don't rely on the data there!

Rails.application.configure do
config.after_initialize do
Bullet.enable = true
Bullet.bullet_logger = true
Bullet.raise = true # raise an error if n+1 query occurs
end

# Settings specified here will take precedence over those in config/application.rb.

# While tests run files are not watched, reloading is not necessary.
Expand Down Expand Up @@ -61,4 +67,7 @@

# Raise error when a before_action's only/except options reference missing actions
config.action_controller.raise_on_missing_callback_actions = true

Rails.application.routes.default_url_options[:host] = 'localhost'
Rails.application.routes.default_url_options[:port] = 3000
end
15 changes: 15 additions & 0 deletions config/initializers/passwordless.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
Passwordless.configure do |config|
config.default_from_address = "noreply@staffplan.com"
config.parent_mailer = "ActionMailer::Base"
config.restrict_token_reuse = true # A token/link can only be used once
config.token_generator = Passwordless::ShortTokenGenerator.new # Used to generate magic link tokens.

config.expires_at = lambda { 1.year.from_now } # How long until a signed in session expires.
config.timeout_at = lambda { 10.minutes.from_now } # How long until a token/magic link times out.

config.redirect_back_after_sign_in = true # When enabled the user will be redirected to their previous page, or a page specified by the `destination_path` query parameter, if available.
config.redirect_to_response_options = {} # Additional options for redirects.
config.success_redirect_path = '/' # After a user successfully signs in
config.failure_redirect_path = '/' # After a sign in fails
config.sign_out_redirect_path = '/' # After a user signs out
end
9 changes: 5 additions & 4 deletions config/routes.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
Rails.application.routes.draw do
# Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html

# Reveal health status on /up that returns 200 if the app boots with no exceptions, otherwise 500.
# Can be used by load balancers and uptime monitors to verify that the app is live.
get "up" => "rails/health#show", as: :rails_health_check

# Defines the root path route ("/")
# root "posts#index"
passwordless_for :users, at: '/', as: :auth

resource :dashboard, only: [:show], controller: "dashboard"

root "dashboard#show"
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# frozen_string_literal: true

# This migration comes from passwordless_engine (originally 20171104221735)
class CreatePasswordlessSessions < ActiveRecord::Migration[5.1]
def change
create_table(:passwordless_sessions) do |t|
t.belongs_to(
:authenticatable,
polymorphic: true,
index: {name: "authenticatable"}
)

t.datetime(:timeout_at, null: false)
t.datetime(:expires_at, null: false)
t.datetime(:claimed_at)
t.string(:token_digest, null: false)
t.string(:identifier, null: false, index: {unique: true}, length: 36)

t.timestamps
end
end
end
11 changes: 11 additions & 0 deletions db/migrate/20231119204431_create_users.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
class CreateUsers < ActiveRecord::Migration[7.1]
def change
create_table :users do |t|
t.string :name
t.string :email
t.timestamps

t.index :email, unique: true
end
end
end
24 changes: 23 additions & 1 deletion db/schema.rb

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

Loading

0 comments on commit 62ceb78

Please sign in to comment.