diff --git a/app/models/identity.rb b/app/models/identity.rb
index 70bc089eb..f5daf3be1 100644
--- a/app/models/identity.rb
+++ b/app/models/identity.rb
@@ -6,7 +6,6 @@ class Identity < UntenantedRecord
has_many :sessions, dependent: :destroy
normalizes :email_address, with: ->(value) { value.strip.downcase }
- validates :email_address, presence: true
def send_magic_link
magic_links.create!.tap do |magic_link|
diff --git a/app/models/user.rb b/app/models/user.rb
index 2046b64d5..fabb0224a 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -3,8 +3,6 @@ class User < ApplicationRecord
Mentionable, Named, Notifiable, Role, Searcher, Watcher
include Timelined # Depends on Accessor
- self.ignored_columns = %i[ password_digest ]
-
has_one_attached :avatar
belongs_to :membership, optional: true
@@ -18,8 +16,6 @@ class User < ApplicationRecord
has_many :pins, dependent: :destroy
has_many :pinned_cards, through: :pins, source: :card
- normalizes :email_address, with: ->(value) { value.strip.downcase }
-
delegate :staff?, to: :identity, allow_nil: true
def deactivate
diff --git a/app/views/account/settings/_user.html.erb b/app/views/account/settings/_user.html.erb
index 78395ff1c..45fc77c07 100644
--- a/app/views/account/settings/_user.html.erb
+++ b/app/views/account/settings/_user.html.erb
@@ -3,7 +3,7 @@
<%= avatar_preview_tag user, hidden_for_screen_reader: true %>
<%= user.name %>
-
<%= user.email_address %>
+
<%= user.identity.email_address %>
<% end %>
diff --git a/app/views/collections/_access_toggle.erb b/app/views/collections/_access_toggle.erb
index 824cace65..829efa05e 100644
--- a/app/views/collections/_access_toggle.erb
+++ b/app/views/collections/_access_toggle.erb
@@ -3,7 +3,7 @@
<%= avatar_preview_tag user, hidden_for_screen_reader: true %>
<%= user.name %>
-
<%= user.email_address %>
+
<%= user.identity.email_address %>
<% end %>
diff --git a/app/views/users/_user.json.jbuilder b/app/views/users/_user.json.jbuilder
index 6b11459f3..6a49bd1de 100644
--- a/app/views/users/_user.json.jbuilder
+++ b/app/views/users/_user.json.jbuilder
@@ -1,6 +1,7 @@
json.cache! user do
- json.(user, :id, :name, :email_address, :role, :active)
+ json.(user, :id, :name, :role, :active)
+ json.email_address user.identity.email_address
json.created_at user.created_at.utc
json.url user_url(user)
diff --git a/app/views/users/show.html.erb b/app/views/users/show.html.erb
index 1ae06e2c0..d5cbd8865 100644
--- a/app/views/users/show.html.erb
+++ b/app/views/users/show.html.erb
@@ -19,7 +19,7 @@
<%= @user.name %>
<% if @user.active? %>
- <%= mail_to @user.email_address %>
+ <%= mail_to @user.identity.email_address %>
<% else %>
<%= @user.name %> is no longer on this account
<% end %>
diff --git a/db/migrate/20251103125353_remove_email_address_and_password_digest_from_users.rb b/db/migrate/20251103125353_remove_email_address_and_password_digest_from_users.rb
new file mode 100644
index 000000000..c336b5b9c
--- /dev/null
+++ b/db/migrate/20251103125353_remove_email_address_and_password_digest_from_users.rb
@@ -0,0 +1,6 @@
+class RemoveEmailAddressAndPasswordDigestFromUsers < ActiveRecord::Migration[8.2]
+ def change
+ remove_column :users, :email_address, :string
+ remove_column :users, :password_digest, :string
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 049e45a1a..4c6901d26 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema[8.2].define(version: 2025_11_02_115338) do
+ActiveRecord::Schema[8.2].define(version: 2025_11_03_125353) do
create_table "accesses", force: :cascade do |t|
t.datetime "accessed_at"
t.integer "collection_id", null: false
@@ -389,13 +389,10 @@ ActiveRecord::Schema[8.2].define(version: 2025_11_02_115338) do
create_table "users", force: :cascade do |t|
t.boolean "active", default: true, null: false
t.datetime "created_at", null: false
- t.string "email_address"
t.integer "membership_id"
t.string "name", null: false
- t.string "password_digest"
t.string "role", default: "member", null: false
t.datetime "updated_at", null: false
- t.index ["email_address"], name: "index_users_on_email_address", unique: true
t.index ["membership_id"], name: "index_users_on_membership_id"
t.index ["role"], name: "index_users_on_role"
end
diff --git a/db/schema_cache.yml b/db/schema_cache.yml
index e4c5ab87c..78c8e718f 100644
--- a/db/schema_cache.yml
+++ b/db/schema_cache.yml
@@ -1045,16 +1045,6 @@ columns:
collation:
comment:
- *7
- - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column
- auto_increment:
- name: email_address
- cast_type: *5
- sql_type_metadata: *6
- 'null': true
- default:
- default_function:
- collation:
- comment:
- *8
- !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column
auto_increment:
@@ -1067,16 +1057,6 @@ columns:
collation:
comment:
- *10
- - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column
- auto_increment:
- name: password_digest
- cast_type: *5
- sql_type_metadata: *6
- 'null': true
- default:
- default_function:
- collation:
- comment:
- !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column
auto_increment:
name: role
@@ -2715,22 +2695,6 @@ indexes:
comment:
valid: true
users:
- - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition
- table: users
- name: index_users_on_email_address
- unique: true
- columns:
- - email_address
- lengths: {}
- orders: {}
- opclasses: {}
- where:
- type:
- using:
- include:
- nulls_not_distinct:
- comment:
- valid: true
- !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition
table: users
name: index_users_on_membership_id
@@ -2879,4 +2843,4 @@ indexes:
nulls_not_distinct:
comment:
valid: true
-version: 20251102115338
+version: 20251103125353
diff --git a/db/seeds.rb b/db/seeds.rb
index 2d1002463..bf40aaf0a 100644
--- a/db/seeds.rb
+++ b/db/seeds.rb
@@ -33,7 +33,7 @@ def create_tenant(signal_account_name)
end
def find_or_create_user(full_name, email_address)
- if user = User.find_by(email_address: email_address)
+ if user = Identity.find_by(email_address: email_address)&.memberships&.find_by(tenant: ApplicationRecord.current_tenant)&.user
user
else
identity = Identity.find_or_create_by!(email_address: email_address)
diff --git a/db/untenanted_migrate/20251103125952_make_email_addresses_mandatory_on_identities.rb b/db/untenanted_migrate/20251103125952_make_email_addresses_mandatory_on_identities.rb
new file mode 100644
index 000000000..615f0320f
--- /dev/null
+++ b/db/untenanted_migrate/20251103125952_make_email_addresses_mandatory_on_identities.rb
@@ -0,0 +1,5 @@
+class MakeEmailAddressesMandatoryOnIdentities < ActiveRecord::Migration[8.2]
+ def change
+ change_column_null :identities, :email_address, false
+ end
+end
diff --git a/db/untenanted_schema.rb b/db/untenanted_schema.rb
index 9e38879e1..b4582291a 100644
--- a/db/untenanted_schema.rb
+++ b/db/untenanted_schema.rb
@@ -10,10 +10,10 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema[8.2].define(version: 2025_10_27_131911) do
+ActiveRecord::Schema[8.2].define(version: 2025_11_03_125952) do
create_table "identities", force: :cascade do |t|
t.datetime "created_at", null: false
- t.string "email_address"
+ t.string "email_address", null: false
t.datetime "updated_at", null: false
t.index ["email_address"], name: "index_identities_on_email_address", unique: true
end
diff --git a/test/models/account_test.rb b/test/models/account_test.rb
index 99711439d..785799c90 100644
--- a/test/models/account_test.rb
+++ b/test/models/account_test.rb
@@ -13,6 +13,8 @@ class AccountTest < ActiveSupport::TestCase
end
test ".create_with_admin_user creates a new local account" do
+ membership = memberships(:david_in_37signals)
+
ApplicationRecord.create_tenant("account-create-with-dependents") do
account = Account.create_with_admin_user(
account: {
@@ -21,7 +23,7 @@ class AccountTest < ActiveSupport::TestCase
},
owner: {
name: "David",
- email_address: "david@37signals.com"
+ membership: membership
}
)
assert_not_nil account
@@ -37,7 +39,7 @@ class AccountTest < ActiveSupport::TestCase
admin = User.find_by(role: "admin")
assert_equal "David", admin.name
- assert_equal "david@37signals.com", admin.email_address
+ assert_equal "david@37signals.com", admin.identity.email_address
assert_equal "admin", admin.role
end
end
diff --git a/test/models/user/accessor_test.rb b/test/models/user/accessor_test.rb
index 5ca1f834f..3305c37c9 100644
--- a/test/models/user/accessor_test.rb
+++ b/test/models/user/accessor_test.rb
@@ -2,7 +2,7 @@ require "test_helper"
class User::AccessorTest < ActiveSupport::TestCase
test "new users get added to all_access collections on creation" do
- user = User.create!(name: "Jorge", email_address: "testregular@example.com")
+ user = User.create!(name: "Jorge")
assert_includes user.collections, collections(:writebook)
assert_equal Collection.all_access.count, user.collections.count
diff --git a/test/models/user/configurable_test.rb b/test/models/user/configurable_test.rb
index dbc353936..b49b78c1b 100644
--- a/test/models/user/configurable_test.rb
+++ b/test/models/user/configurable_test.rb
@@ -2,7 +2,7 @@ require "test_helper"
class User::ConfigurableTest < ActiveSupport::TestCase
test "should create settings for new users" do
- user = User.create! name: "Some new user", email_address: "some.new@user.com"
+ user = User.create! name: "Some new user"
assert user.settings.present?
end
end
diff --git a/test/models/user_test.rb b/test/models/user_test.rb
index 23f604ffb..2f3e71dca 100644
--- a/test/models/user_test.rb
+++ b/test/models/user_test.rb
@@ -4,8 +4,7 @@ class UserTest < ActiveSupport::TestCase
test "create" do
user = User.create! \
role: "member",
- name: "Victor Cooper",
- email_address: "victor@hey.com"
+ name: "Victor Cooper"
assert_equal [ collections(:writebook) ], user.collections
assert user.settings.present?
@@ -14,8 +13,7 @@ class UserTest < ActiveSupport::TestCase
test "creation gives access to all_access collections" do
user = User.create! \
role: "member",
- name: "Victor Cooper",
- email_address: "victor@hey.com"
+ name: "Victor Cooper"
assert_equal [ collections(:writebook) ], user.collections
end