sequel_auth 0.0.7 → 0.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 +4 -4
- data/lib/sequel_auth/version.rb +1 -1
- data/lib/sequel_auth.rb +163 -41
- metadata +64 -17
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0b90735774160726580b1d8bbf0b4897b1ae02e5cccebacb41b18c526fed90c4
|
|
4
|
+
data.tar.gz: e23bc0c9be9ae661ae2f6e36b838322c328fe67118bcb617de5f1631f3a3a2e5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 408aab64849e9286363b6a99621a0a738f517794100b6c31f19e47bdde2ec822113cb4bcfaede000ee6e7894017e18e20c74d54d55fec77d602280acc94cbe9e
|
|
7
|
+
data.tar.gz: 9b70e674fe932228d544d7293d849bf2e5d0b158bec008d043518b1a27cdfd388b4219aabb9909b997bddb55746d71e435da578168546470b3ccefbce97c0d44
|
data/lib/sequel_auth/version.rb
CHANGED
data/lib/sequel_auth.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require "sequel"
|
|
2
4
|
require "securerandom"
|
|
3
5
|
require_relative "sequel_auth/providers/bcrypt"
|
|
@@ -7,86 +9,206 @@ require_relative "sequel_auth/providers/crypt"
|
|
|
7
9
|
module Sequel
|
|
8
10
|
module Plugins
|
|
9
11
|
module SequelAuth
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
# Provider factory method
|
|
13
|
+
def self.provider(provider, opts = {})
|
|
14
|
+
provider_class = Kernel.const_get("SequelAuth::Providers::#{provider.to_s.capitalize}")
|
|
15
|
+
opts.each { |k, v| provider_class.public_send("#{k}=", v) }
|
|
16
|
+
provider_class
|
|
14
17
|
end
|
|
15
18
|
|
|
19
|
+
# Default password strength options
|
|
20
|
+
DEFAULT_STRENGTH_OPTIONS = {
|
|
21
|
+
min_length: 8,
|
|
22
|
+
require_lowercase: true,
|
|
23
|
+
require_uppercase: true,
|
|
24
|
+
require_number: true,
|
|
25
|
+
require_special_char: false,
|
|
26
|
+
max_length: 128
|
|
27
|
+
}.freeze
|
|
28
|
+
|
|
29
|
+
# Plugin configuration
|
|
16
30
|
def self.configure(model, opts = {})
|
|
17
31
|
model.instance_eval do
|
|
32
|
+
# Core configuration
|
|
18
33
|
@digest_column = opts.fetch(:digest_column, :password_digest)
|
|
19
34
|
@include_validations = opts.fetch(:include_validations, true)
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
35
|
+
|
|
36
|
+
# Provider configuration
|
|
37
|
+
@provider = SequelAuth.provider(
|
|
38
|
+
opts.fetch(:provider, :bcrypt),
|
|
39
|
+
opts.fetch(:provider_opts, {})
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
# Optional columns
|
|
23
43
|
@access_token_column = opts.fetch(:access_token_column, nil)
|
|
24
44
|
@login_count_column = opts.fetch(:login_count_column, nil)
|
|
25
45
|
@failed_login_count_column = opts.fetch(:failed_login_count_column, nil)
|
|
26
|
-
@last_login_at_column = opts.fetch(:last_login_at_column,nil)
|
|
46
|
+
@last_login_at_column = opts.fetch(:last_login_at_column, nil)
|
|
47
|
+
|
|
48
|
+
# Password strength configuration
|
|
49
|
+
if opts[:password_strength]
|
|
50
|
+
@password_strength_options = SequelAuth.validate_strength_options(opts[:password_strength])
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Validate strength options
|
|
56
|
+
def self.validate_strength_options(options)
|
|
57
|
+
# Merge with defaults
|
|
58
|
+
merged = DEFAULT_STRENGTH_OPTIONS.merge(options)
|
|
59
|
+
|
|
60
|
+
# Validate options
|
|
61
|
+
unless merged[:min_length].is_a?(Integer) && merged[:min_length] > 0
|
|
62
|
+
raise ArgumentError, ':min_length must be a positive integer'
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
unless merged[:max_length].is_a?(Integer) && merged[:max_length] >= merged[:min_length]
|
|
66
|
+
raise ArgumentError, ':max_length must be an integer greater than or equal to min_length'
|
|
27
67
|
end
|
|
68
|
+
|
|
69
|
+
[ :require_lowercase, :require_uppercase, :require_number, :require_special_char ].each do |key|
|
|
70
|
+
unless [true, false].include?(merged[key])
|
|
71
|
+
raise ArgumentError, ":#{key} must be true or false"
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
merged.freeze
|
|
28
76
|
end
|
|
29
77
|
|
|
78
|
+
# Class methods
|
|
30
79
|
module ClassMethods
|
|
31
|
-
attr_reader :provider,
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
80
|
+
attr_reader :provider,
|
|
81
|
+
:digest_column,
|
|
82
|
+
:access_token_column,
|
|
83
|
+
:login_count_column,
|
|
84
|
+
:failed_login_count_column,
|
|
85
|
+
:last_login_at_column,
|
|
86
|
+
:include_validations,
|
|
87
|
+
:password_strength_options
|
|
38
88
|
|
|
39
|
-
#
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
:@digest_column => nil)
|
|
89
|
+
# Check if password strength validation is enabled
|
|
90
|
+
def password_strength_enabled?
|
|
91
|
+
!@password_strength_options.nil?
|
|
92
|
+
end
|
|
44
93
|
|
|
94
|
+
# Inherit instance variables
|
|
95
|
+
Plugins.inherited_instance_variables(
|
|
96
|
+
self,
|
|
97
|
+
:@provider => nil,
|
|
98
|
+
:@include_validations => nil,
|
|
99
|
+
:@digest_column => nil,
|
|
100
|
+
:@password_strength_options => nil
|
|
101
|
+
)
|
|
45
102
|
end
|
|
46
103
|
|
|
104
|
+
# Instance methods
|
|
47
105
|
module InstanceMethods
|
|
48
106
|
attr_accessor :password_confirmation
|
|
49
107
|
attr_reader :password
|
|
108
|
+
|
|
109
|
+
# Password setter with encryption
|
|
50
110
|
def password=(unencrypted)
|
|
51
111
|
@password = unencrypted
|
|
52
|
-
self.send
|
|
112
|
+
self.send("#{model.digest_column}=", model.provider.encrypt(unencrypted))
|
|
53
113
|
end
|
|
54
114
|
|
|
115
|
+
# Authentication method
|
|
55
116
|
def authenticate(unencrypted)
|
|
56
|
-
if model.provider.matches?(self.send(model.digest_column),unencrypted)
|
|
57
|
-
|
|
58
|
-
#Update login count
|
|
59
|
-
self.send("#{model.login_count_column}=",self.send(model.login_count_column)+1 ) if model.login_count_column
|
|
60
|
-
self.send("#{model.last_login_at_column}=",Time.now ) if model.last_login_at_column
|
|
61
|
-
self.save
|
|
62
|
-
end
|
|
117
|
+
if model.provider.matches?(self.send(model.digest_column), unencrypted)
|
|
118
|
+
update_login_success
|
|
63
119
|
self
|
|
64
120
|
else
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
self.send("#{model.failed_login_count_column}=",self.send(model.failed_login_count_column)+1 )
|
|
68
|
-
self.save
|
|
69
|
-
end
|
|
121
|
+
update_login_failure
|
|
122
|
+
nil
|
|
70
123
|
end
|
|
71
124
|
end
|
|
72
125
|
|
|
126
|
+
# Access token reset
|
|
73
127
|
def reset_access_token
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
128
|
+
return unless model.access_token_column
|
|
129
|
+
|
|
130
|
+
self.update(model.access_token_column => SecureRandom.urlsafe_base64(16))
|
|
77
131
|
end
|
|
78
132
|
|
|
133
|
+
# Validation method
|
|
79
134
|
def validate
|
|
80
135
|
super
|
|
81
136
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
137
|
+
run_basic_validations
|
|
138
|
+
run_password_strength_validations if model.password_strength_enabled?
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
private
|
|
142
|
+
|
|
143
|
+
# Basic validations
|
|
144
|
+
def run_basic_validations
|
|
145
|
+
return unless model.include_validations
|
|
146
|
+
|
|
147
|
+
# Password presence validation (only for new records)
|
|
148
|
+
if password.nil? && new?
|
|
149
|
+
errors.add :password, 'is not present'
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Password confirmation validation
|
|
153
|
+
if password != password_confirmation
|
|
154
|
+
errors.add :password, "doesn't match confirmation"
|
|
85
155
|
end
|
|
86
156
|
end
|
|
87
157
|
|
|
158
|
+
# Password strength validations
|
|
159
|
+
def run_password_strength_validations
|
|
160
|
+
return if password.nil? || password.empty?
|
|
161
|
+
|
|
162
|
+
options = model.password_strength_options
|
|
163
|
+
|
|
164
|
+
# Check minimum length
|
|
165
|
+
if password.length < options[:min_length]
|
|
166
|
+
errors.add :password, "must be at least #{options[:min_length]} characters long"
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# Check maximum length
|
|
170
|
+
if password.length > options[:max_length]
|
|
171
|
+
errors.add :password, "must be at most #{options[:max_length]} characters long"
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# Check for lowercase letters
|
|
175
|
+
if options[:require_lowercase] && !password.match(/[a-z]/)
|
|
176
|
+
errors.add :password, 'must contain at least one lowercase letter'
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# Check for uppercase letters
|
|
180
|
+
if options[:require_uppercase] && !password.match(/[A-Z]/)
|
|
181
|
+
errors.add :password, 'must contain at least one uppercase letter'
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# Check for numbers
|
|
185
|
+
if options[:require_number] && !password.match(/\d/)
|
|
186
|
+
errors.add :password, 'must contain at least one number'
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# Check for special characters
|
|
190
|
+
if options[:require_special_char] && !password.match(/[^A-Za-z0-9]/)
|
|
191
|
+
errors.add :password, 'must contain at least one special character'
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# Update login success counters
|
|
196
|
+
def update_login_success
|
|
197
|
+
return unless model.login_count_column || model.last_login_at_column
|
|
198
|
+
|
|
199
|
+
self.send("#{model.login_count_column}=", self.send(model.login_count_column) + 1) if model.login_count_column
|
|
200
|
+
self.send("#{model.last_login_at_column}=", Time.now) if model.last_login_at_column
|
|
201
|
+
self.save
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
# Update login failure counters
|
|
205
|
+
def update_login_failure
|
|
206
|
+
return unless model.failed_login_count_column
|
|
207
|
+
|
|
208
|
+
self.send("#{model.failed_login_count_column}=", self.send(model.failed_login_count_column) + 1)
|
|
209
|
+
self.save
|
|
210
|
+
end
|
|
88
211
|
end
|
|
89
|
-
|
|
90
212
|
end
|
|
91
213
|
end
|
|
92
|
-
end
|
|
214
|
+
end
|
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: sequel_auth
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0
|
|
4
|
+
version: 0.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Fatih GENÇ
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 2026-04-16 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: sequel
|
|
@@ -16,20 +15,20 @@ dependencies:
|
|
|
16
15
|
requirements:
|
|
17
16
|
- - "~>"
|
|
18
17
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: '5.
|
|
18
|
+
version: '5.60'
|
|
20
19
|
- - ">="
|
|
21
20
|
- !ruby/object:Gem::Version
|
|
22
|
-
version: 5.
|
|
21
|
+
version: 5.60.0
|
|
23
22
|
type: :runtime
|
|
24
23
|
prerelease: false
|
|
25
24
|
version_requirements: !ruby/object:Gem::Requirement
|
|
26
25
|
requirements:
|
|
27
26
|
- - "~>"
|
|
28
27
|
- !ruby/object:Gem::Version
|
|
29
|
-
version: '5.
|
|
28
|
+
version: '5.60'
|
|
30
29
|
- - ">="
|
|
31
30
|
- !ruby/object:Gem::Version
|
|
32
|
-
version: 5.
|
|
31
|
+
version: 5.60.0
|
|
33
32
|
- !ruby/object:Gem::Dependency
|
|
34
33
|
name: bcrypt
|
|
35
34
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -37,6 +36,9 @@ dependencies:
|
|
|
37
36
|
- - "~>"
|
|
38
37
|
- !ruby/object:Gem::Version
|
|
39
38
|
version: '3.1'
|
|
39
|
+
- - ">="
|
|
40
|
+
- !ruby/object:Gem::Version
|
|
41
|
+
version: 3.1.0
|
|
40
42
|
type: :runtime
|
|
41
43
|
prerelease: false
|
|
42
44
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -44,6 +46,9 @@ dependencies:
|
|
|
44
46
|
- - "~>"
|
|
45
47
|
- !ruby/object:Gem::Version
|
|
46
48
|
version: '3.1'
|
|
49
|
+
- - ">="
|
|
50
|
+
- !ruby/object:Gem::Version
|
|
51
|
+
version: 3.1.0
|
|
47
52
|
- !ruby/object:Gem::Dependency
|
|
48
53
|
name: scrypt
|
|
49
54
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -51,6 +56,9 @@ dependencies:
|
|
|
51
56
|
- - "~>"
|
|
52
57
|
- !ruby/object:Gem::Version
|
|
53
58
|
version: '3.0'
|
|
59
|
+
- - ">="
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: 3.0.0
|
|
54
62
|
type: :runtime
|
|
55
63
|
prerelease: false
|
|
56
64
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -58,48 +66,89 @@ dependencies:
|
|
|
58
66
|
- - "~>"
|
|
59
67
|
- !ruby/object:Gem::Version
|
|
60
68
|
version: '3.0'
|
|
69
|
+
- - ">="
|
|
70
|
+
- !ruby/object:Gem::Version
|
|
71
|
+
version: 3.0.0
|
|
61
72
|
- !ruby/object:Gem::Dependency
|
|
62
73
|
name: rspec
|
|
63
74
|
requirement: !ruby/object:Gem::Requirement
|
|
64
75
|
requirements:
|
|
65
76
|
- - "~>"
|
|
66
77
|
- !ruby/object:Gem::Version
|
|
67
|
-
version: '3.
|
|
78
|
+
version: '3.12'
|
|
79
|
+
- - ">="
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: 3.12.0
|
|
68
82
|
type: :development
|
|
69
83
|
prerelease: false
|
|
70
84
|
version_requirements: !ruby/object:Gem::Requirement
|
|
71
85
|
requirements:
|
|
72
86
|
- - "~>"
|
|
73
87
|
- !ruby/object:Gem::Version
|
|
74
|
-
version: '3.
|
|
88
|
+
version: '3.12'
|
|
89
|
+
- - ">="
|
|
90
|
+
- !ruby/object:Gem::Version
|
|
91
|
+
version: 3.12.0
|
|
75
92
|
- !ruby/object:Gem::Dependency
|
|
76
93
|
name: simplecov
|
|
77
94
|
requirement: !ruby/object:Gem::Requirement
|
|
78
95
|
requirements:
|
|
79
96
|
- - "~>"
|
|
80
97
|
- !ruby/object:Gem::Version
|
|
81
|
-
version: '0.
|
|
98
|
+
version: '0.22'
|
|
99
|
+
- - ">="
|
|
100
|
+
- !ruby/object:Gem::Version
|
|
101
|
+
version: 0.22.0
|
|
82
102
|
type: :development
|
|
83
103
|
prerelease: false
|
|
84
104
|
version_requirements: !ruby/object:Gem::Requirement
|
|
85
105
|
requirements:
|
|
86
106
|
- - "~>"
|
|
87
107
|
- !ruby/object:Gem::Version
|
|
88
|
-
version: '0.
|
|
108
|
+
version: '0.22'
|
|
109
|
+
- - ">="
|
|
110
|
+
- !ruby/object:Gem::Version
|
|
111
|
+
version: 0.22.0
|
|
89
112
|
- !ruby/object:Gem::Dependency
|
|
90
113
|
name: sqlite3
|
|
91
114
|
requirement: !ruby/object:Gem::Requirement
|
|
92
115
|
requirements:
|
|
93
116
|
- - "~>"
|
|
94
117
|
- !ruby/object:Gem::Version
|
|
95
|
-
version: '1.
|
|
118
|
+
version: '1.7'
|
|
119
|
+
- - ">="
|
|
120
|
+
- !ruby/object:Gem::Version
|
|
121
|
+
version: 1.7.0
|
|
122
|
+
type: :development
|
|
123
|
+
prerelease: false
|
|
124
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
125
|
+
requirements:
|
|
126
|
+
- - "~>"
|
|
127
|
+
- !ruby/object:Gem::Version
|
|
128
|
+
version: '1.7'
|
|
129
|
+
- - ">="
|
|
130
|
+
- !ruby/object:Gem::Version
|
|
131
|
+
version: 1.7.0
|
|
132
|
+
- !ruby/object:Gem::Dependency
|
|
133
|
+
name: rake
|
|
134
|
+
requirement: !ruby/object:Gem::Requirement
|
|
135
|
+
requirements:
|
|
136
|
+
- - "~>"
|
|
137
|
+
- !ruby/object:Gem::Version
|
|
138
|
+
version: '13.0'
|
|
139
|
+
- - ">="
|
|
140
|
+
- !ruby/object:Gem::Version
|
|
141
|
+
version: 13.0.0
|
|
96
142
|
type: :development
|
|
97
143
|
prerelease: false
|
|
98
144
|
version_requirements: !ruby/object:Gem::Requirement
|
|
99
145
|
requirements:
|
|
100
146
|
- - "~>"
|
|
101
147
|
- !ruby/object:Gem::Version
|
|
102
|
-
version: '
|
|
148
|
+
version: '13.0'
|
|
149
|
+
- - ">="
|
|
150
|
+
- !ruby/object:Gem::Version
|
|
151
|
+
version: 13.0.0
|
|
103
152
|
description: Plugin to add authentication methods to Sequel Model
|
|
104
153
|
email: fatihgnc@gmail.com
|
|
105
154
|
executables: []
|
|
@@ -115,7 +164,6 @@ homepage: https://gitlab.com/ikilifikir/sequel_auth
|
|
|
115
164
|
licenses:
|
|
116
165
|
- MIT
|
|
117
166
|
metadata: {}
|
|
118
|
-
post_install_message:
|
|
119
167
|
rdoc_options: []
|
|
120
168
|
require_paths:
|
|
121
169
|
- lib
|
|
@@ -123,15 +171,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
123
171
|
requirements:
|
|
124
172
|
- - ">="
|
|
125
173
|
- !ruby/object:Gem::Version
|
|
126
|
-
version:
|
|
174
|
+
version: 2.7.0
|
|
127
175
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
128
176
|
requirements:
|
|
129
177
|
- - ">="
|
|
130
178
|
- !ruby/object:Gem::Version
|
|
131
179
|
version: '0'
|
|
132
180
|
requirements: []
|
|
133
|
-
rubygems_version:
|
|
134
|
-
signing_key:
|
|
181
|
+
rubygems_version: 4.0.10
|
|
135
182
|
specification_version: 4
|
|
136
183
|
summary: Plugin to add authentication methods to Sequel Model Select one of crypt,
|
|
137
184
|
bcrypt, scrypt
|