Allow local authentication with LOCAL_AUTHENTICATION=1

You can touch `tmp/local-auth.txt` to persist this setting.
This commit is contained in:
Mike Dalessio
2025-08-06 14:10:33 -04:00
parent 0b938a8375
commit 57269b1b9c
15 changed files with 178 additions and 11 deletions
+4 -4
View File
@@ -7,6 +7,8 @@ module Authentication
before_action :require_authentication
helper_method :authenticated?
include LoginHelper
end
class_methods do
@@ -53,11 +55,9 @@ module Authentication
def request_authentication(untenanted: false)
if ApplicationRecord.current_tenant.present?
session[:return_to_after_authenticating] = request.url
redirect_to Launchpad.login_url(product: true, account: Account.sole), allow_other_host: true
else
# Don't save the current untenanted URL, because it's just going to bounce back to Launchpad after login anyway.
redirect_to Launchpad.login_url(product: true), allow_other_host: true
end
redirect_to_login_url
end
def after_authentication_url
+22 -1
View File
@@ -1,6 +1,27 @@
class SessionsController < ApplicationController
before_action :require_local_auth, only: %i[ new create ]
require_unauthenticated_access only: %i[ new create ]
rate_limit to: 10, within: 3.minutes, only: :create, with: -> { redirect_to new_session_path, alert: "Try again later." }
def new
end
def create
if user = User.active.authenticate_by(params.permit(:email_address, :password))
start_new_session_for user
redirect_to after_authentication_url
else
redirect_to new_session_path, alert: "Try another email address or password."
end
end
def destroy
terminate_session
redirect_to Launchpad.login_url, allow_other_host: true
redirect_to_login_url
end
private
def require_local_auth
head :forbidden unless Rails.application.config.x.local_authentication
end
end
@@ -1,5 +1,6 @@
class Signup::BaseController < ApplicationController
require_untenanted_access
before_action :redirect_if_local_auth
# TODO: Remove this auth before launch.
http_basic_authenticate_with(
@@ -33,4 +34,8 @@ class Signup::BaseController < ApplicationController
redirect_to account.signal_account.owner.remote_login_url(proceed_to: root_path),
allow_other_host: true
end
def redirect_if_local_auth
render plain: "Unauthorized", status: :unauthorized if Rails.application.config.x.local_authentication
end
end
+17
View File
@@ -0,0 +1,17 @@
module LoginHelper
def login_url
if ApplicationRecord.current_tenant
if Rails.application.config.x.local_authentication
new_session_path
else
Launchpad.login_url(product: true, account: Account.sole)
end
else
Launchpad.login_url(product: true)
end
end
def redirect_to_login_url
redirect_to login_url, allow_other_host: true
end
end
+3 -1
View File
@@ -2,7 +2,9 @@ module Account::SignalAccount
extend ActiveSupport::Concern
included do
belongs_to :signal_account, class_name: "SignalId::Account", primary_key: :queenbee_id, foreign_key: :queenbee_id, optional: true
unless Rails.application.config.x.local_authentication
belongs_to :signal_account, class_name: "SignalId::Account", primary_key: :queenbee_id, foreign_key: :queenbee_id, optional: true
end
end
class_methods do
+3 -1
View File
@@ -2,6 +2,8 @@ module User::SignalUser
extend ActiveSupport::Concern
included do
belongs_to :signal_user, dependent: :destroy, class_name: "SignalId::User", optional: true
unless Rails.application.config.x.local_authentication
belongs_to :signal_user, dependent: :destroy, class_name: "SignalId::User", optional: true
end
end
end
+30
View File
@@ -0,0 +1,30 @@
<% @page_title = "Sign in" %>
<div class="panel shadow center margin-block-double <%= "shake" if flash[:alert] %>" style="--panel-size: 55ch;">
<h1 class="txt-xx-large margin-block-end-double">Fizzy</h1>
<%= form_with url: session_path, class: "flex flex-column gap txt-large" do |form| %>
<div class="flex align-center gap">
<label class="flex align-center gap input input--actor">
<%= form.email_field :email_address, required: true, class: "input full-width", autofocus: true, autocomplete: "username", placeholder: "Enter your email address" %>
<%= icon_tag "email", class: "txt-large" %>
</label>
</div>
<div class="flex align-center gap">
<label class="flex align-center gap input input--actor">
<%= form.password_field :password, required: true, class: "input full-width", autocomplete: "current-password", placeholder: "Enter your password", maxlength: 72 %>
<%= icon_tag "password", class: "txt-large" %>
</label>
</div>
<button type="submit" id="log_in" class="btn btn--reversed center">
<%= icon_tag "arrow-right" %>
<span class="for-screen-reader">Sign in</span>
</button>
<% end %>
</div>
<% content_for :footer do %>
<div class="txt-align-center center margin-block-double txt-subtle">Fizzy&trade; version 0</div>
<% end %>
+1 -1
View File
@@ -52,7 +52,7 @@
<footer class="margin-block-start-double txt-center">
<p class="txt-small txt-muted">
Already have an account?
<%= link_to "Sign in", Launchpad.login_url(product: true), class: "decorated" %>
<%= link_to "Sign in", login_url, class: "decorated" %>
</p>
</footer>
</div>
+1 -1
View File
@@ -4,7 +4,7 @@
<nav class="header">
<div class="header__actions header__actions--start"></div>
<div class="header__actions header__actions--end">
<%= link_to Launchpad.login_url(account: Account.sole), class: "btn", data: { controller: "hotkey", action: "keydown.esc@document->hotkey#click" } do %>
<%= link_to login_url, class: "btn", data: { controller: "hotkey", action: "keydown.esc@document->hotkey#click" } do %>
<span class="for-screen-reader">Sign in instead</span>
<% end %>
</div>
+4
View File
@@ -8,4 +8,8 @@ if [ -f tmp/solid-queue.txt ]; then
export SOLID_QUEUE_IN_PUMA=1
fi
if [ -f tmp/local-auth.txt ]; then
export LOCAL_AUTHENTICATION=1
fi
exec ./bin/rails server -p 3006
+7
View File
@@ -1,11 +1,18 @@
require "signal_id"
Rails.application.config.x.local_authentication = ENV["LOCAL_AUTHENTICATION"].present?
ENV["SIGNAL_ID_SECRET"] = Rails.application.credentials.signal_id_secret
Rails.application.config.to_prepare do
SignalId.product = "fizzy"
db_config = SignalId::Database.default_configuration
if Rails.application.config.x.local_authentication
db_config.each do |name, config|
config["connect_timeout"] = 1
end
end
SignalId::Database.load_configuration db_config
SignalId::Database.enable_rw_splitting!
+1 -1
View File
@@ -89,7 +89,7 @@ Rails.application.routes.draw do
get "join/:join_code", to: "users#new", as: :join
post "join/:join_code", to: "users#create"
resource :session, only: :destroy do
resource :session do
scope module: "sessions" do
resources :transfers, only: %i[ show update ]
resource :launchpad, only: %i[ show update ], controller: "launchpad"
@@ -15,6 +15,24 @@ class ControllerAuthenticationTest < ActionDispatch::IntegrationTest
assert_redirected_to Launchpad.login_url(product: true, account: Account.sole)
end
test "local auth, access without an account slug redirects to launchpad" do
integration_session.default_url_options[:script_name] = "" # no tenant
with_local_auth do
get cards_path
end
assert_redirected_to Launchpad.login_url(product: true)
end
test "local auth, access with an account slug but no session redirects to new session" do
with_local_auth do
get cards_path
end
assert_redirected_to new_session_path
end
test "access with an account slug and a session allows functional access" do
sign_in_as :kevin
+52 -1
View File
@@ -3,8 +3,59 @@ require "test_helper"
class SessionsControllerTest < ActionDispatch::IntegrationTest
test "destroy" do
sign_in_as :kevin
delete session_path
assert_redirected_to Launchpad.login_url
assert_redirected_to Launchpad.login_url(account: Account.sole)
assert_not cookies[:session_token].present?
end
test "local auth, destroy" do
sign_in_as :kevin
with_local_auth do
delete session_path
end
assert_redirected_to new_session_path
assert_not cookies[:session_token].present?
end
test "new" do
get new_session_path
assert_response :forbidden
end
test "local auth, new" do
with_local_auth do
get new_session_path
end
assert_response :success
end
test "create" do
post session_path, params: { email_address: "david@37signals.com", password: "secret123456" }
assert_response :forbidden
end
test "local auth, create with valid credentials" do
with_local_auth do
post session_path, params: { email_address: "david@37signals.com", password: "secret123456" }
end
assert_redirected_to root_path
assert cookies[:session_token].present?
end
test "local auth, create with invalid credentials" do
with_local_auth do
post session_path, params: { email_address: "david@37signals.com", password: "wrong" }
end
assert_redirected_to new_session_path
assert_not cookies[:session_token].present?
end
end
+10
View File
@@ -29,6 +29,16 @@ module ActiveSupport
include ActiveJob::TestHelper
include SignalId::Testing
include ActionTextTestHelper, CardTestHelper, ChangeTestHelper, SessionTestHelper
def with_local_auth
begin
old_local_auth = Rails.application.config.x.local_authentication
Rails.application.config.x.local_authentication = true
yield
ensure
Rails.application.config.x.local_authentication = old_local_auth
end
end
end
end