veri 0.2.0 → 0.2.2

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: '0782d4bd585ca9ca0aaa91cb51fe4b240d9e97ccf829c69890bc2079b38dda18'
4
- data.tar.gz: 56cd6f6147a7b23ea2a5cc0b12dd9d573fcae55dcbf49462f93a4db59d5ef724
3
+ metadata.gz: 33fc3ba81b0ff4f44df7e5bf8e6f6e25c5c318d8b1ca02988f200bf0039d19b9
4
+ data.tar.gz: 1433809afa4da2090dac9c75d8e1d6a14579c3ad77ff8ae6db49a5291e708b68
5
5
  SHA512:
6
- metadata.gz: 29763c6104a41dd4987ff30d4c75089d8390d5c36afb7d9f9f297fe627380f0b674d46d4c52aedb27865776d6915bd38ca4acacc2adda7894fe66d9e457bfeec
7
- data.tar.gz: '038ae16e36b31d87b4b83f7d51f51bb4c9d9318e6c161c9249f6eeff0a6a970343a2b1acbcb256edb4622bd03e01d1b6dda5c90c441274ef3b841f792dbc8ba6'
6
+ metadata.gz: 86fcd9a468d1f4fcb0c7fbe820c4e67d40a6c5e96a50bb2804141c4ad9960c5f42e0678bdd6bdf8284e9403c7d77e41fb0f756c78cf4acd019d6094aef95212a
7
+ data.tar.gz: e50a7bd3673bc9d806e2ed8f3e50820b75373df52720407ccf400073de0ff726ec1e8e89694369878f06b83f8cf8516fdad5ece1645dd62f0afd6ff74e36df95
data/CHANGELOG.md CHANGED
@@ -1,3 +1,16 @@
1
+ ## v0.2.2
2
+
3
+ ### Bugs
4
+
5
+ - Fixed class resolution in `current_user` method
6
+
7
+ ## v0.2.1
8
+
9
+ ### Misc
10
+
11
+ - Enhanced error messages
12
+ - Performance improvements and code optimizations
13
+
1
14
  ## v0.2.0
2
15
 
3
16
  ### Breaking
@@ -10,31 +10,66 @@ module Veri
10
10
  setting :hashing_algorithm,
11
11
  default: :argon2,
12
12
  reader: true,
13
- constructor: -> (value) { Veri::Inputs.process(value, as: :hashing_algorithm, error: Veri::ConfigurationError) }
13
+ constructor: -> (value) do
14
+ Veri::Inputs.process(
15
+ value,
16
+ as: :hashing_algorithm,
17
+ error: Veri::ConfigurationError,
18
+ message: "Invalid hashing algorithm `#{value.inspect}`, supported algorithms are: #{Veri::Configuration::HASHERS.keys.join(", ")}"
19
+ )
20
+ end
14
21
  setting :inactive_session_lifetime,
15
22
  default: nil,
16
23
  reader: true,
17
- constructor: -> (value) { Veri::Inputs.process(value, as: :duration, optional: true, error: Veri::ConfigurationError) }
24
+ constructor: -> (value) do
25
+ Veri::Inputs.process(
26
+ value,
27
+ as: :duration,
28
+ optional: true,
29
+ error: Veri::ConfigurationError,
30
+ message: "Invalid inactive session lifetime `#{value.inspect}`, expected an instance of ActiveSupport::Duration or nil"
31
+ )
32
+ end
18
33
  setting :total_session_lifetime,
19
34
  default: 14.days,
20
35
  reader: true,
21
- constructor: -> (value) { Veri::Inputs.process(value, as: :duration, error: Veri::ConfigurationError) }
36
+ constructor: -> (value) do
37
+ Veri::Inputs.process(
38
+ value,
39
+ as: :duration,
40
+ error: Veri::ConfigurationError,
41
+ message: "Invalid total session lifetime `#{value.inspect}`, expected an instance of ActiveSupport::Duration"
42
+ )
43
+ end
22
44
  setting :user_model_name,
23
45
  default: "User",
24
46
  reader: true,
25
- constructor: -> (value) { Veri::Inputs.process(value, as: :string, error: Veri::ConfigurationError) }
47
+ constructor: -> (value) do
48
+ Veri::Inputs.process(
49
+ value,
50
+ as: :non_empty_string,
51
+ error: Veri::ConfigurationError,
52
+ message: "Invalid user model name `#{value.inspect}`, expected an ActiveRecord model name as a string"
53
+ )
54
+ end
55
+
56
+ HASHERS = {
57
+ argon2: Veri::Password::Argon2,
58
+ bcrypt: Veri::Password::BCrypt,
59
+ scrypt: Veri::Password::SCrypt
60
+ }.freeze
26
61
 
27
62
  def hasher
28
- case hashing_algorithm
29
- when :argon2 then Veri::Password::Argon2
30
- when :bcrypt then Veri::Password::BCrypt
31
- when :scrypt then Veri::Password::SCrypt
32
- else raise Veri::Error, "Invalid hashing algorithm: #{hashing_algorithm}"
33
- end
63
+ HASHERS.fetch(hashing_algorithm) { raise Veri::Error, "Invalid hashing algorithm: #{hashing_algorithm}" }
34
64
  end
35
65
 
36
66
  def user_model
37
- Veri::Inputs.process(user_model_name, as: :model, error: Veri::ConfigurationError)
67
+ Veri::Inputs.process(
68
+ user_model_name,
69
+ as: :model,
70
+ error: Veri::ConfigurationError,
71
+ message: "Invalid user model name `#{user_model_name}`, expected an ActiveRecord model name as a string"
72
+ )
38
73
  end
39
74
  end
40
75
  end
@@ -23,7 +23,9 @@ module Veri
23
23
  end
24
24
 
25
25
  def current_user
26
- @current_user ||= current_session&.identity
26
+ user_model = Veri::Configuration.user_model
27
+ primary_key = user_model.primary_key
28
+ @current_user ||= current_session ? user_model.find_by(primary_key => current_session.authenticatable_id) : nil
27
29
  end
28
30
 
29
31
  def current_session
@@ -32,7 +34,12 @@ module Veri
32
34
  end
33
35
 
34
36
  def log_in(authenticatable)
35
- token = Veri::Session.establish(Veri::Inputs.process(authenticatable, as: :authenticatable), request)
37
+ processed_authenticatable = Veri::Inputs.process(
38
+ authenticatable,
39
+ as: :authenticatable,
40
+ message: "Expected an instance of #{Veri::Configuration.user_model_name}, got `#{authenticatable.inspect}`"
41
+ )
42
+ token = Veri::Session.establish(processed_authenticatable, request)
36
43
  cookies.encrypted.permanent[:veri_token] = { value: token, httponly: true }
37
44
  end
38
45
 
data/lib/veri/inputs.rb CHANGED
@@ -6,22 +6,26 @@ module Veri
6
6
 
7
7
  include Dry.Types()
8
8
 
9
- def process(value, as:, optional: false, error: Veri::InvalidArgumentError)
10
- checker = send(as)
9
+ TYPES = {
10
+ hashing_algorithm: -> { self::Strict::Symbol.enum(:argon2, :bcrypt, :scrypt) },
11
+ duration: -> { self::Instance(ActiveSupport::Duration) },
12
+ non_empty_string: -> { self::Strict::String.constrained(min_size: 1) },
13
+ model: -> { self::Strict::Class.constructor { _1.try(:safe_constantize) || _1 }.constrained(lt: ActiveRecord::Base) },
14
+ authenticatable: -> { self::Instance(Veri::Configuration.user_model) },
15
+ request: -> { self::Instance(ActionDispatch::Request) }
16
+ }.freeze
17
+
18
+ def process(value, as:, optional: false, error: Veri::InvalidArgumentError, message: nil)
19
+ checker = type_for(as)
11
20
  checker = checker.optional if optional
12
21
 
13
22
  checker[value]
14
23
  rescue Dry::Types::CoercionError => e
15
- raise error, e.message
24
+ raise error, message || e.message
16
25
  end
17
26
 
18
27
  private
19
28
 
20
- def hashing_algorithm = self::Strict::Symbol.enum(:argon2, :bcrypt, :scrypt)
21
- def duration = self::Instance(ActiveSupport::Duration)
22
- def string = self::Strict::String
23
- def model = self::Strict::Class.constructor { _1.try(:safe_constantize) || _1 }.constrained(lt: ActiveRecord::Base)
24
- def authenticatable = self::Instance(Veri::Configuration.user_model)
25
- def request = self::Instance(ActionDispatch::Request)
29
+ def type_for(name) = Veri::Inputs::TYPES.fetch(name).call
26
30
  end
27
31
  end
@@ -12,17 +12,32 @@ module Veri
12
12
 
13
13
  def update_password(password)
14
14
  update!(
15
- hashed_password: hasher.create(Veri::Inputs.process(password, as: :string)),
15
+ hashed_password: hasher.create(
16
+ Veri::Inputs.process(
17
+ password,
18
+ as: :non_empty_string,
19
+ message: "Expected a non-empty string, got `#{password.inspect}`"
20
+ )
21
+ ),
16
22
  password_updated_at: Time.current
17
23
  )
18
24
  end
19
25
 
20
26
  def verify_password(password)
21
- hasher.verify(Veri::Inputs.process(password, as: :string), hashed_password)
27
+ hasher.verify(
28
+ Veri::Inputs.process(
29
+ password,
30
+ as: :non_empty_string,
31
+ message: "Expected a non-empty string, got `#{password.inspect}`"
32
+ ),
33
+ hashed_password
34
+ )
22
35
  end
23
36
 
24
37
  private
25
38
 
26
- def hasher = Veri::Configuration.hasher
39
+ def hasher
40
+ @hasher ||= Veri::Configuration.hasher
41
+ end
27
42
  end
28
43
  end
@@ -55,7 +55,11 @@ module Veri
55
55
  update!(
56
56
  shapeshifted_at: Time.current,
57
57
  original_authenticatable: authenticatable,
58
- authenticatable: Veri::Inputs.process(user, as: :authenticatable)
58
+ authenticatable: Veri::Inputs.process(
59
+ user,
60
+ as: :authenticatable,
61
+ message: "Expected an instance of #{Veri::Configuration.user_model_name}, got `#{user.inspect}`"
62
+ )
59
63
  )
60
64
  end
61
65
 
@@ -68,14 +72,14 @@ module Veri
68
72
  end
69
73
 
70
74
  class << self
71
- def establish(authenticatable, request)
75
+ def establish(user, request)
72
76
  token = SecureRandom.hex(32)
73
77
  expires_at = Time.current + Veri::Configuration.total_session_lifetime
74
78
 
75
79
  new(
76
80
  hashed_token: Digest::SHA256.hexdigest(token),
77
81
  expires_at:,
78
- authenticatable: Veri::Inputs.process(authenticatable, as: :authenticatable, error: Veri::Error)
82
+ authenticatable: Veri::Inputs.process(user, as: :authenticatable, error: Veri::Error)
79
83
  ).update_info(
80
84
  Veri::Inputs.process(request, as: :request, error: Veri::Error)
81
85
  )
@@ -83,20 +87,36 @@ module Veri
83
87
  token
84
88
  end
85
89
 
86
- def prune(authenticatable = nil)
87
- processed_authenticatable = Veri::Inputs.process(authenticatable, as: :authenticatable, optional: true)
88
- scope = processed_authenticatable ? where(authenticatable: processed_authenticatable) : all
89
- to_be_pruned = scope.where(expires_at: ...Time.current)
90
+ def prune(user = nil)
91
+ scope = if user
92
+ where(
93
+ authenticatable: Veri::Inputs.process(
94
+ user,
95
+ as: :authenticatable,
96
+ optional: true,
97
+ message: "Expected an instance of #{Veri::Configuration.user_model_name} or nil, got `#{user.inspect}`"
98
+ )
99
+ )
100
+ else
101
+ all
102
+ end
103
+
104
+ expired_scope = scope.where(expires_at: ...Time.current)
105
+
90
106
  if Veri::Configuration.inactive_session_lifetime
91
- to_be_pruned = to_be_pruned.or(
92
- scope.where(last_seen_at: ...(Time.current - Veri::Configuration.inactive_session_lifetime))
93
- )
107
+ inactive_cutoff = Time.current - Veri::Configuration.inactive_session_lifetime
108
+ expired_scope = expired_scope.or(scope.where(last_seen_at: ...inactive_cutoff))
94
109
  end
95
- to_be_pruned.delete_all
110
+
111
+ expired_scope.delete_all
96
112
  end
97
113
 
98
- def terminate_all(authenticatable)
99
- Veri::Inputs.process(authenticatable, as: :authenticatable).veri_sessions.delete_all
114
+ def terminate_all(user)
115
+ Veri::Inputs.process(
116
+ user,
117
+ as: :authenticatable,
118
+ message: "Expected an instance of #{Veri::Configuration.user_model_name}, got `#{user.inspect}`"
119
+ ).veri_sessions.delete_all
100
120
  end
101
121
  end
102
122
  end
data/lib/veri/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Veri
2
- VERSION = "0.2.0"
2
+ VERSION = "0.2.2".freeze
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: veri
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - enjaku4