Signup workflow for existing identity
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
class Signup::AccountsController < ApplicationController
|
||||
require_untenanted_access
|
||||
class Signup::AccountsController < Signup::BaseController
|
||||
before_action :reset_signup_storage
|
||||
|
||||
def new
|
||||
@signup = Signup.new
|
||||
@@ -8,7 +8,10 @@ class Signup::AccountsController < ApplicationController
|
||||
def create
|
||||
@signup = Signup.new(signup_params)
|
||||
|
||||
if @signup.process
|
||||
if @signup.recognized?
|
||||
store_signup
|
||||
redirect_to Launchpad.authentication_url(purpose: "signup", login_hint: @signup.email_address, redirect_uri: signup_session_url), allow_other_host: true
|
||||
elsif @signup.process
|
||||
redirect_to_account(@signup.account)
|
||||
else
|
||||
render :new, status: :unprocessable_entity
|
||||
@@ -17,11 +20,10 @@ class Signup::AccountsController < ApplicationController
|
||||
|
||||
private
|
||||
def signup_params
|
||||
params.require(:signup).permit(:full_name, :email_address, :password, :company_name)
|
||||
params.require(:signup).permit(*Signup::PERMITTED_KEYS)
|
||||
end
|
||||
|
||||
def redirect_to_account(account)
|
||||
redirect_to account.signal_account.owner.remote_login_url(proceed_to: root_path),
|
||||
allow_other_host: true
|
||||
def store_signup
|
||||
@signup.to_h.each { |key, value| signup_storage[key.to_s] = value }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
class Signup::BaseController < ApplicationController
|
||||
require_untenanted_access
|
||||
|
||||
attr_reader :authenticated_identity
|
||||
|
||||
private
|
||||
def signup_storage
|
||||
session[:signup] ||= {}
|
||||
end
|
||||
|
||||
def reset_signup_storage
|
||||
session.delete(:signup)
|
||||
end
|
||||
|
||||
def authenticated_identity=(identity)
|
||||
@authenticated_identity = identity
|
||||
signup_storage["identity_id"] = identity.try(:id)
|
||||
end
|
||||
|
||||
def set_authenticated_identity
|
||||
if identity_id = signup_storage["identity_id"]
|
||||
@authenticated_identity = SignalId::Identity.find_by(id: identity_id)
|
||||
end
|
||||
end
|
||||
|
||||
def redirect_to_account(account)
|
||||
redirect_to account.signal_account.owner.remote_login_url(proceed_to: root_path),
|
||||
allow_other_host: true
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,23 @@
|
||||
class Signup::CompletionsController < Signup::BaseController
|
||||
before_action :set_authenticated_identity, only: :create
|
||||
|
||||
def new
|
||||
end
|
||||
|
||||
def create
|
||||
@signup = Signup.new(signup_params)
|
||||
@signup.signal_identity = authenticated_identity
|
||||
|
||||
if @signup.process
|
||||
reset_signup_storage
|
||||
redirect_to_account(@signup.account)
|
||||
else
|
||||
render plain: "Could not complete signup. This is probably a bug.", status: :unprocessable_entity, layout: false
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def signup_params
|
||||
signup_storage.slice(*Signup::PERMITTED_KEYS.map(&:to_s))
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,9 @@
|
||||
class Signup::SessionsController < Signup::BaseController
|
||||
def create
|
||||
if self.authenticated_identity = SignalId::Identity.authenticate(params.permit(:sig))
|
||||
redirect_to new_signup_completion_url
|
||||
else
|
||||
render plain: "Authentication failed. This is probably a bug.", status: :unauthorized
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -8,6 +8,10 @@ module Launchpad
|
||||
url product_account_path("/signin", product: product, account: account), params
|
||||
end
|
||||
|
||||
def authentication_url(**params)
|
||||
url "/authenticate", params.merge(product: :fizzy)
|
||||
end
|
||||
|
||||
def product_account_path(path = nil, product: false, account: nil)
|
||||
product_path = "/fizzy" if product || account
|
||||
account_path = "/#{account.signal_account.id}" if account
|
||||
|
||||
@@ -3,6 +3,8 @@ class Signup
|
||||
include ActiveModel::Attributes
|
||||
include ActiveModel::Validations
|
||||
|
||||
PERMITTED_KEYS = %i[ full_name email_address password company_name ]
|
||||
|
||||
# Input attribute
|
||||
attr_accessor :company_name
|
||||
|
||||
@@ -18,6 +20,10 @@ class Signup
|
||||
validate :validate_new_identity
|
||||
|
||||
def initialize(...)
|
||||
@full_name = nil
|
||||
@email_address = nil
|
||||
@password = nil
|
||||
@company_name = nil
|
||||
@signal_identity = nil
|
||||
@queenbee_account = nil
|
||||
@account = nil
|
||||
@@ -44,10 +50,22 @@ class Signup
|
||||
false
|
||||
end
|
||||
|
||||
def recognized?
|
||||
SignalId::Identity.find_by_email_address(email_address).present?
|
||||
end
|
||||
|
||||
def tenant_name
|
||||
@tenant_name ||= signal_account.subdomain
|
||||
end
|
||||
|
||||
def to_h
|
||||
{
|
||||
full_name: full_name,
|
||||
email_address: email_address,
|
||||
company_name: company_name
|
||||
}.compact_blank
|
||||
end
|
||||
|
||||
private
|
||||
def create_signal_identity
|
||||
unless signal_identity.present?
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<%= auto_submit_form_with url: signup_completions_path, data: { turbo: false } %>
|
||||
@@ -91,6 +91,8 @@ Rails.application.routes.draw do
|
||||
namespace :signup do
|
||||
get "/" => "accounts#new"
|
||||
resources :accounts, only: %i[ new create ]
|
||||
get "/session" => "sessions#create" # redirect from Launchpad after mid-signup authentication
|
||||
resources :completions, only: %i[ new create ]
|
||||
end
|
||||
|
||||
resources :users do
|
||||
|
||||
@@ -36,4 +36,43 @@ class Signup::AccountsControllerTest < ActionDispatch::IntegrationTest
|
||||
signal_account = SignalId::Account.last
|
||||
assert_redirected_to(/#{signal_account.login_url}/)
|
||||
end
|
||||
|
||||
test "create for an existing identity" do
|
||||
integration_session.host = "example.com" # no subdomain
|
||||
|
||||
identity = signal_identities(:david)
|
||||
|
||||
post signup_accounts_url, params: { signup: { email_address: identity.email_address, company_name: "signup-accounts-controller-test-2" } }
|
||||
assert_authentication_requested_for identity
|
||||
|
||||
assert_no_difference -> { SignalId::Identity.count } do
|
||||
assert_difference -> { SignalId::Account.count } do
|
||||
authenticate_via_launchpad_as(identity)
|
||||
assert_redirected_to_account
|
||||
assert_nil session[:signup]
|
||||
end
|
||||
end
|
||||
|
||||
signal_account = SignalId::Account.last
|
||||
ApplicationRecord.with_tenant(signal_account.subdomain) do
|
||||
assert_equal Account.last, signal_account.peer
|
||||
end
|
||||
end
|
||||
|
||||
def assert_authentication_requested_for(identity)
|
||||
assert_response :redirect
|
||||
assert_match(/#{Regexp.escape(Launchpad.url("/authenticate", login_hint: identity.email_address))}.*&purpose=signup/, redirect_to_url)
|
||||
end
|
||||
|
||||
def authenticate_via_launchpad_as(identity)
|
||||
get signup_session_url, params: { sig: identity.perishable_signature }
|
||||
assert_equal identity.id, session[:signup]["identity_id"]
|
||||
assert_redirected_to new_signup_completion_url
|
||||
post signup_completions_url
|
||||
end
|
||||
|
||||
def assert_redirected_to_account(signal_account = SignalId::Account.last)
|
||||
assert_response :redirect
|
||||
assert_match(/#{Regexp.escape(signal_account.url("/session/launchpad"))}.*&sig=/, redirect_to_url)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -10,6 +10,16 @@ class SignupTest < ActiveSupport::TestCase
|
||||
)
|
||||
end
|
||||
|
||||
test "#to_h allows persistence of the signup data" do
|
||||
actual = @signup.to_h
|
||||
expected = {
|
||||
email_address: @signup.email_address,
|
||||
full_name: @signup.full_name,
|
||||
company_name: @signup.company_name
|
||||
}
|
||||
assert_equal expected, actual
|
||||
end
|
||||
|
||||
test "#process creates all the necessary objects for a new identity" do
|
||||
Account.any_instance.expects(:setup_basic_template).once
|
||||
|
||||
|
||||
Reference in New Issue
Block a user