standard_id 0.56.0 → 0.57.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c50961cc2c4446bd495efdebca4ad54a871c371fab3c63e0ff1dd6792b44e1ff
|
|
4
|
+
data.tar.gz: be9ebdaedf84eedfed63f98ade1e8fb37bfc90112b7ba4532aa8d179aefb03b9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 55f8de3f738e3363e452842bcea0b68fbff7798e59039bdac698de7b0b5d1c1fffdb58fe693e60671aa6c9cc2743059fc3d3269e11bde54a84d7bbb23e64b8f5
|
|
7
|
+
data.tar.gz: 9c0e08fdb33cc59058c5be1be6f65b7308bb497738a8a338de1072e5cbd105f59417cbd65c4902e6f937ff53544bbd755cf7e8b8e349b2bd058583cc75eaa2bc
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,31 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.57.0] - 2026-10-28
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **Migration `20261028000000_add_value_lookup_indexes_to_standard_id_tables`.** Adds a
|
|
15
|
+
`[type, value]` index on `standard_id_identifiers`, which serves the `find_by(value:)` lookup behind
|
|
16
|
+
every sign-in, OTP start, social login and password reset (the only index containing `value` led
|
|
17
|
+
with `account_id`, so each lookup was a sequential scan). Also adds a partial
|
|
18
|
+
`client_application_id` index on `standard_id_refresh_tokens` for `PurgeStaleDynamicClientsJob`.
|
|
19
|
+
Both are built `CONCURRENTLY` on PostgreSQL and are idempotent. Run
|
|
20
|
+
`bin/rails standard_id:install:migrations && bin/rails db:migrate`.
|
|
21
|
+
|
|
22
|
+
## [0.56.1] - 2026-10-27
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
|
|
26
|
+
- **`credential.password.created` from the API email + password sign-up now carries a usable
|
|
27
|
+
credential chain.** `EmailPassword::Registration` created the `Credential` row with
|
|
28
|
+
`Credential.create!`, but `PasswordCredential`'s `has_one :credential, touch: true` had already
|
|
29
|
+
read (and cached) the association as `nil` in `after_create`. A subscriber walking
|
|
30
|
+
`event[:credential].credential.identifier.account` got `nil`; it now gets the account, with the
|
|
31
|
+
whole chain loaded in memory, so strict-loading hosts never load it lazily.
|
|
32
|
+
`Web::SignupForm` was unaffected (it builds the chain with nested attributes); it now has the
|
|
33
|
+
same regression spec.
|
|
34
|
+
|
|
10
35
|
## [0.56.0] - 2026-10-26
|
|
11
36
|
|
|
12
37
|
### Added
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
class AddValueLookupIndexesToStandardIdTables < ActiveRecord::Migration[8.0]
|
|
2
|
+
# Indexes for two lookups nothing in the schema serves (deep audit
|
|
3
|
+
# DBP-002/DBP-011, 2026-10-27):
|
|
4
|
+
#
|
|
5
|
+
# - standard_id_identifiers [type, value]: every sign-in, OTP start, social
|
|
6
|
+
# login and password reset resolves its identifier with
|
|
7
|
+
# `EmailIdentifier.find_by(value:)` / `PhoneNumberIdentifier.find_by(value:)`
|
|
8
|
+
# (STI adds `type = ?`). The only index containing `value` leads with
|
|
9
|
+
# account_id, so each lookup is a sequential scan. Not unique: the same
|
|
10
|
+
# value may belong to more than one account.
|
|
11
|
+
# - standard_id_refresh_tokens client_application_id: PurgeStaleDynamicClientsJob
|
|
12
|
+
# reads refresh tokens per client (`client.refresh_tokens`) and by
|
|
13
|
+
# `client_application_id IS NOT NULL`. Partial, because tokens not bound
|
|
14
|
+
# to an application (social sign-in refreshes, rows minted before
|
|
15
|
+
# 20261020000000) leave it NULL.
|
|
16
|
+
#
|
|
17
|
+
# At today's table sizes these scans are cheap (Sentry, 14 days: the
|
|
18
|
+
# identifier lookup is p50 2-4ms, p95 9-15ms in every host); the cost grows
|
|
19
|
+
# with every account. Idempotent (if_not_exists / if_exists), CONCURRENTLY
|
|
20
|
+
# on Postgres, and an INVALID index left by a failed concurrent build is
|
|
21
|
+
# dropped and rebuilt, same conventions as 20260924000000.
|
|
22
|
+
disable_ddl_transaction!
|
|
23
|
+
|
|
24
|
+
INDEXES = [
|
|
25
|
+
{
|
|
26
|
+
table: :standard_id_identifiers,
|
|
27
|
+
columns: [:type, :value],
|
|
28
|
+
name: "index_standard_id_identifiers_on_type_and_value"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
table: :standard_id_refresh_tokens,
|
|
32
|
+
columns: :client_application_id,
|
|
33
|
+
name: "index_standard_id_refresh_tokens_on_client_application_id",
|
|
34
|
+
where: "client_application_id IS NOT NULL"
|
|
35
|
+
}
|
|
36
|
+
].freeze
|
|
37
|
+
|
|
38
|
+
def up
|
|
39
|
+
INDEXES.each do |index|
|
|
40
|
+
if postgres? && invalid_postgres_index?(index[:name])
|
|
41
|
+
remove_index index[:table], name: index[:name], if_exists: true, **concurrent
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
add_index index[:table], index[:columns],
|
|
45
|
+
name: index[:name], where: index[:where], if_not_exists: true, **concurrent
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def down
|
|
50
|
+
INDEXES.each do |index|
|
|
51
|
+
remove_index index[:table], name: index[:name], if_exists: true, **concurrent
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
def postgres?
|
|
58
|
+
connection.adapter_name.downcase.include?("postgres")
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def concurrent
|
|
62
|
+
postgres? ? { algorithm: :concurrently } : {}
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def invalid_postgres_index?(name)
|
|
66
|
+
select_value(<<~SQL.squish).present?
|
|
67
|
+
SELECT 1
|
|
68
|
+
FROM pg_index i
|
|
69
|
+
INNER JOIN pg_class c ON c.oid = i.indexrelid
|
|
70
|
+
WHERE c.relname = #{connection.quote(name)} AND NOT i.indisvalid
|
|
71
|
+
SQL
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -105,7 +105,13 @@ module StandardId
|
|
|
105
105
|
account: account, value: email, signup_source: EmailPassword::SIGNUP_SOURCE
|
|
106
106
|
)
|
|
107
107
|
password_credential = StandardId::PasswordCredential.create!(login: email, password: password)
|
|
108
|
-
|
|
108
|
+
# Created through the association, not Credential.create!: the
|
|
109
|
+
# has_one's touch: true reads password_credential.credential in
|
|
110
|
+
# after_create, caching nil, so a subscriber walking
|
|
111
|
+
# credential.identifier.account would get nil. Going through the
|
|
112
|
+
# association leaves the whole chain loaded in memory, which also
|
|
113
|
+
# keeps strict-loading hosts from lazily loading identifier/account.
|
|
114
|
+
password_credential.create_credential!(identifier: identifier)
|
|
109
115
|
|
|
110
116
|
StandardId::Events.publish(
|
|
111
117
|
StandardId::Events::ACCOUNT_CREATED,
|
data/lib/standard_id/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: standard_id
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.57.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jaryl Sim
|
|
@@ -249,6 +249,7 @@ files:
|
|
|
249
249
|
- db/migrate/20261020000000_add_client_application_id_to_standard_id_refresh_tokens.rb
|
|
250
250
|
- db/migrate/20261020120000_add_registration_access_token_digest_to_standard_id_client_applications.rb
|
|
251
251
|
- db/migrate/20261022000000_add_signup_source_to_standard_id_identifiers.rb
|
|
252
|
+
- db/migrate/20261028000000_add_value_lookup_indexes_to_standard_id_tables.rb
|
|
252
253
|
- lib/generators/standard_id/install/install_generator.rb
|
|
253
254
|
- lib/generators/standard_id/install/templates/standard_id.rb
|
|
254
255
|
- lib/generators/standard_id/install/templates/standard_id_reference.rb
|