veri 2.0.3 → 2.1.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: 90c72152f71aea2555de8f7788b356e8b957d94df73a0bfc826ee79b1087fed7
4
- data.tar.gz: f552a61efc8b704a9065bca3d785dcb8cd4046299ee94e05dc0cb08b381511e4
3
+ metadata.gz: 703094a8e90e3fde6888629b9702406548122bed4ab199e1a02eac174c68ee07
4
+ data.tar.gz: 337701b97ed23c8cffd7b8ffb6d649e3e520c09bc61de6ed69e7c9d8b1905462
5
5
  SHA512:
6
- metadata.gz: 1947901336055c81c82f36c18c6abdeadc76d6d1ce47564de69bd83cb98a75d14e433276df5be08c24506277571121d81eaf6c3c0805c5d1041f8d9272821d31
7
- data.tar.gz: eb186110a8816965a3ae0c7f396fdd8b0fd212ce281602139268e55cf466e3ad7589e0838383840e629c2b6259ab027c1beb343b5f4338b778717a3b5670eaae
6
+ metadata.gz: e717d0cf3a4a8d9c673919548c739820ed15b42e7ab7140c9a95cd0f4740cd20a1678cf2a9dfbbcbaeb1257b60f9b8b2a28c2030d9b893fbf382a309e90b1445
7
+ data.tar.gz: 515e5fee62f96f4ddf4982419207565a905faa00246b5d1b483581ba86fd1063e0bbe06eaa253274c6e73d69395b1f8afd296210afd8aed3c067e264d872d7a4
data/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ ## v2.1.0
2
+
3
+ ### Features
4
+
5
+ - Added `Veri::Session#switch_tenant` method to move a session to another tenant
6
+ - Added `password_set?` method to check whether a user has a password
7
+
8
+ ### Bugs
9
+
10
+ - Fixed `log_in` using the previous tenant when `current_tenant` changes within the same request
11
+ - Fixed `Veri::Session.in_tenant` raising an error without a message when given an invalid tenant
12
+
1
13
  ## v2.0.3
2
14
 
3
15
  ### Bugs
data/README.md CHANGED
@@ -93,6 +93,9 @@ user.update_password("password")
93
93
 
94
94
  # Verify a password
95
95
  user.verify_password("password")
96
+
97
+ # Check if a password is set
98
+ user.password_set?
96
99
  ```
97
100
  Changing a password does not automatically terminate existing sessions. If you want to invalidate the user's sessions after a password change, do so explicitly:
98
101
 
@@ -398,6 +401,40 @@ user.sessions.in_tenant(tenant)
398
401
  user.sessions.in_tenant(tenant).terminate_all
399
402
  ```
400
403
 
404
+ ### Switching Tenants
405
+
406
+ A session belongs to one tenant, and a browser holds one session per host. A user is therefore logged in to one tenant at a time, unless your tenants live on separate subdomains, where each subdomain keeps its own session.
407
+
408
+ To move a user to another tenant, call `switch_tenant` on their session and make `current_tenant` return the new tenant from then on:
409
+
410
+ ```rb
411
+ class ApplicationController < ActionController::Base
412
+ include Veri::Authentication
413
+
414
+ with_authentication
415
+
416
+ private
417
+
418
+ def current_tenant
419
+ # This example keeps the tenant in a cookie; your app can keep it anywhere
420
+ Company.find_by(id: cookies.signed[:company_id])
421
+ end
422
+ end
423
+
424
+ class CompaniesController < ApplicationController
425
+ def switch
426
+ company = current_user.companies.find(params[:id])
427
+
428
+ current_session.switch_tenant(company)
429
+ cookies.signed[:company_id] = company.id
430
+
431
+ redirect_to root_path, notice: "Switched to #{company.name}"
432
+ end
433
+ end
434
+ ```
435
+
436
+ The user stays logged in and the session keeps its expiration.
437
+
401
438
  ### User Impersonation with Tenants
402
439
 
403
440
  When using user impersonation in a multi-tenant setup, Veri allows cross-tenant shapeshifting while preserving the original tenant context:
@@ -418,18 +455,12 @@ All other session methods work the same way in multi-tenant applications as in s
418
455
 
419
456
  When a tenant object is deleted from your database, its associated sessions become orphaned.
420
457
 
421
- To clean up orphaned sessions, use:
458
+ Ideally, the application should be designed so that this doesn't happen: whenever a tenant object is deleted, its sessions are terminated as part of the same operation. However, if that wasn't done, you can clean up the orphaned sessions as a fallback:
422
459
 
423
460
  ```rb
424
461
  Veri::Session.prune
425
462
  ```
426
463
 
427
- Or, for a specific user:
428
-
429
- ```rb
430
- user.sessions.prune
431
- ```
432
-
433
464
  ### Tenant Migrations
434
465
 
435
466
  When you rename or remove models used as tenants, you need to update Veri's stored data accordingly. Use these irreversible data migrations:
@@ -33,10 +33,7 @@ module Veri
33
33
  end
34
34
 
35
35
  def log_in(authenticatable)
36
- processed_authenticatable = Veri::Inputs::Authenticatable.new(
37
- authenticatable,
38
- message: "Expected an instance of #{Veri::Configuration.user_model_name}, got `#{authenticatable.inspect}`"
39
- ).process
36
+ processed_authenticatable = Veri::Inputs::Authenticatable.new(authenticatable).process
40
37
 
41
38
  return false if processed_authenticatable.locked?
42
39
 
@@ -87,11 +84,7 @@ module Veri
87
84
  def current_tenant = nil
88
85
 
89
86
  def resolved_tenant
90
- @resolved_tenant ||= Veri::Inputs::Tenant.new(
91
- current_tenant,
92
- error: Veri::InvalidTenantError,
93
- message: "Expected a string, an ActiveRecord model instance, or nil, got `#{current_tenant.inspect}`"
94
- ).resolve
87
+ Veri::Inputs::Tenant.new(current_tenant).resolve
95
88
  end
96
89
 
97
90
  def reset_memoization
@@ -3,6 +3,8 @@ module Veri
3
3
  class Authenticatable < Veri::Inputs::Base
4
4
  private
5
5
 
6
+ def default_message = "Expected an instance of #{Veri::Configuration.user_model_name}, got `#{@value.inspect}`"
7
+
6
8
  def processor = -> { @value.is_a?(Veri::Configuration.user_model) ? @value : raise_error }
7
9
  end
8
10
  end
@@ -1,7 +1,7 @@
1
1
  module Veri
2
2
  module Inputs
3
3
  class Base
4
- def initialize(value, optional: false, error: Veri::InvalidArgumentError, message: nil)
4
+ def initialize(value, optional: false, error: nil, message: nil)
5
5
  @value = value
6
6
  @optional = optional
7
7
  @error = error
@@ -20,8 +20,11 @@ module Veri
20
20
  raise NotImplementedError
21
21
  end
22
22
 
23
+ def default_error = Veri::InvalidArgumentError
24
+ def default_message = nil
25
+
23
26
  def raise_error
24
- raise @error, @message
27
+ raise @error || default_error, @message || default_message
25
28
  end
26
29
  end
27
30
  end
@@ -3,6 +3,8 @@ module Veri
3
3
  class NonEmptyString < Veri::Inputs::Base
4
4
  private
5
5
 
6
+ def default_message = "Expected a non-empty string, got `#{@value.inspect}`"
7
+
6
8
  def processor = -> { @value.is_a?(String) && !@value.empty? ? @value : raise_error }
7
9
  end
8
10
  end
@@ -17,6 +17,9 @@ module Veri
17
17
 
18
18
  private
19
19
 
20
+ def default_error = Veri::InvalidTenantError
21
+ def default_message = "Expected a string, an ActiveRecord model instance, or nil, got `#{@value.inspect}`"
22
+
20
23
  def processor
21
24
  -> {
22
25
  return @value if @value.nil?
@@ -16,16 +16,20 @@ module Veri
16
16
  def update_password(password)
17
17
  update!(
18
18
  hashed_password: hasher.create(
19
- Veri::Inputs::NonEmptyString.new(password, message: "Expected a non-empty string, got `#{password.inspect}`").process
19
+ Veri::Inputs::NonEmptyString.new(password).process
20
20
  ),
21
21
  password_updated_at: Time.current
22
22
  )
23
23
  end
24
24
 
25
+ def password_set?
26
+ hashed_password.present?
27
+ end
28
+
25
29
  def verify_password(password)
26
- processed_password = Veri::Inputs::NonEmptyString.new(password, message: "Expected a non-empty string, got `#{password.inspect}`").process
30
+ processed_password = Veri::Inputs::NonEmptyString.new(password).process
27
31
 
28
- return false if hashed_password.blank?
32
+ return false unless password_set?
29
33
 
30
34
  stored_hasher = Veri::Configuration::HASHERS.values.find { _1.match?(hashed_password) }
31
35
 
@@ -60,11 +60,7 @@ module Veri
60
60
  def shapeshift(user, tenant: nil)
61
61
  raise Veri::Error, "Cannot shapeshift from a shapeshifted session" if shapeshifted?
62
62
 
63
- resolved_tenant = Veri::Inputs::Tenant.new(
64
- tenant,
65
- error: Veri::InvalidTenantError,
66
- message: "Expected a string, an ActiveRecord model instance, or nil, got `#{tenant.inspect}`"
67
- ).resolve
63
+ resolved_tenant = Veri::Inputs::Tenant.new(tenant).resolve
68
64
 
69
65
  update!(
70
66
  shapeshifted_at: Time.current,
@@ -72,10 +68,7 @@ module Veri
72
68
  original_tenant_type: tenant_type,
73
69
  original_tenant_id: tenant_id,
74
70
  **resolved_tenant,
75
- authenticatable: Veri::Inputs::Authenticatable.new(
76
- user,
77
- message: "Expected an instance of #{Veri::Configuration.user_model_name}, got `#{user.inspect}`"
78
- ).process
71
+ authenticatable: Veri::Inputs::Authenticatable.new(user).process
79
72
  )
80
73
  end
81
74
 
@@ -91,6 +84,10 @@ module Veri
91
84
  )
92
85
  end
93
86
 
87
+ def switch_tenant(tenant)
88
+ update!(**Veri::Inputs::Tenant.new(tenant).resolve)
89
+ end
90
+
94
91
  def tenant
95
92
  resolve_tenant(tenant_type, tenant_id) { super }
96
93
  end
data/lib/veri/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Veri
2
- VERSION = "2.0.3".freeze
2
+ VERSION = "2.1.0".freeze
3
3
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: veri
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.3
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - enjaku4
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2026-07-19 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: argon2
@@ -147,7 +147,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
147
147
  - !ruby/object:Gem::Version
148
148
  version: '0'
149
149
  requirements: []
150
- rubygems_version: 4.0.3
150
+ rubygems_version: 4.0.16
151
151
  specification_version: 4
152
152
  summary: Minimal cookie-based authentication library for Ruby on Rails
153
153
  test_files: []