validators 3.0.5 → 3.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/README.md +31 -2
- data/data/reserved_hostnames.json +1399 -0
- data/lib/validators/locale/en.yml +27 -0
- data/lib/validators/locale/pt-BR.yml +27 -0
- data/lib/validators/reserved_hostnames.rb +49 -0
- data/lib/validators/validates_reserved_hostname.rb +45 -0
- data/lib/validators/validates_reserved_username.rb +29 -0
- data/lib/validators/version.rb +2 -2
- data/lib/validators.rb +6 -0
- data/test/support/models.rb +12 -0
- data/test/test_helper.rb +3 -13
- data/test/validators/disposable_email_test.rb +2 -1
- data/test/validators/validates_reserved_hostname_test.rb +40 -0
- data/test/validators/validates_reserved_username_test.rb +40 -0
- metadata +12 -4
- data/test/support/translations.yml +0 -39
@@ -0,0 +1,27 @@
|
|
1
|
+
---
|
2
|
+
en:
|
3
|
+
activemodel: &activemodel
|
4
|
+
errors:
|
5
|
+
messages:
|
6
|
+
disposable_email: "is not allowed (high-bounce domain)"
|
7
|
+
invalid_cnpj: "is not a valid CNPJ"
|
8
|
+
invalid_cpf: "is not a valid CPF"
|
9
|
+
invalid_date: "is not a valid date"
|
10
|
+
invalid_date_after: "needs to be after %{date}"
|
11
|
+
invalid_date_before: "needs to be before %{date}"
|
12
|
+
invalid_email: "is not a valid address"
|
13
|
+
invalid_hostname: "does not have a valid hostname"
|
14
|
+
invalid_ip_address: "is not a valid IP address"
|
15
|
+
invalid_ipv4_address: "is not a valid IPv4 address"
|
16
|
+
invalid_ipv6_address: "is not a valid IPv6 address"
|
17
|
+
invalid_owner: "is not associated with your user"
|
18
|
+
invalid_ssh_private_key: "is not a valid private SSH key"
|
19
|
+
invalid_ssh_private_key_bits: "needs to be at least %{required} bits; got %{value} bits instead"
|
20
|
+
invalid_ssh_private_key_type: "must be a %{value} key"
|
21
|
+
invalid_ssh_public_key: "is not a valid public SSH key"
|
22
|
+
invalid_url: "is not a valid address"
|
23
|
+
reserved_hostname: "%{value} is a reserved hostname"
|
24
|
+
reserved_username: "%{value} is a reserved username"
|
25
|
+
|
26
|
+
activerecord:
|
27
|
+
<<: *activemodel
|
@@ -0,0 +1,27 @@
|
|
1
|
+
---
|
2
|
+
pt-BR:
|
3
|
+
activemodel: &activemodel
|
4
|
+
errors:
|
5
|
+
messages:
|
6
|
+
disposable_email: "não é permitido (e-mail temporário)"
|
7
|
+
invalid_cnpj: "não é um CNPJ válido"
|
8
|
+
invalid_cpf: "não é um CPF válido"
|
9
|
+
invalid_date: "não é uma data válida"
|
10
|
+
invalid_date_after: "precisa ser depois de %{date}"
|
11
|
+
invalid_date_before: "precisa ser antes de %{date}"
|
12
|
+
invalid_email: "não parece ser um e-mail válido"
|
13
|
+
invalid_hostname: "não é um hostname válido"
|
14
|
+
invalid_ip_address: "não é um endereço IP válido"
|
15
|
+
invalid_ipv4_address: "não é um endereço IPv4 válido"
|
16
|
+
invalid_ipv6_address: "não é um endereço IPv6 válido"
|
17
|
+
invalid_owner: "não está associado ao seu usuário"
|
18
|
+
invalid_ssh_private_key: "não é uma chave privada de SSH válida"
|
19
|
+
invalid_ssh_private_key_bits: "precisa ter pelo menos %{required} bits; a sua chave tem %{value} bits"
|
20
|
+
invalid_ssh_private_key_type: "precisa ser uma chave %{value}"
|
21
|
+
invalid_ssh_public_key: "não é uma chave pública de SSH válida"
|
22
|
+
invalid_url: "não parece ser uma URL válida"
|
23
|
+
reserved_hostname: "%{value} é um hostname reservado"
|
24
|
+
reserved_username: "%{value} é nome de usuário reservado"
|
25
|
+
|
26
|
+
activerecord:
|
27
|
+
<<: *activemodel
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Validators
|
4
|
+
class ReservedHostnames
|
5
|
+
FILE_PATH = File.expand_path("../../data/reserved_hostnames.json", __dir__)
|
6
|
+
|
7
|
+
def self.reserved?(hostname, matchers = nil)
|
8
|
+
matchers = parse_list(matchers) if matchers
|
9
|
+
matchers ||= all
|
10
|
+
match_any?(matchers, hostname)
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.all
|
14
|
+
@all ||= JSON
|
15
|
+
.parse(File.read(FILE_PATH))
|
16
|
+
.map {|matcher| parse(matcher) }
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.parse(matcher)
|
20
|
+
return matcher unless matcher.start_with?("/")
|
21
|
+
|
22
|
+
Regexp.compile(matcher[%r{/(.*?)/}, 1])
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.parse_list(matchers)
|
26
|
+
matchers.map {|matcher| parse(matcher) }
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.match_any?(matchers, hostname)
|
30
|
+
hostname = normalize(hostname)
|
31
|
+
matchers.any? {|matcher| match?(matcher, hostname) }
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.normalize(hostname)
|
35
|
+
hostname.downcase.gsub(/[_-]/, "")
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.match?(matcher, hostname)
|
39
|
+
case matcher
|
40
|
+
when String
|
41
|
+
matcher == hostname
|
42
|
+
when Regexp
|
43
|
+
hostname =~ matcher
|
44
|
+
else
|
45
|
+
raise "Unknown matcher type: #{matcher.class}"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveModel
|
4
|
+
module Validations
|
5
|
+
class ReservedHostnameValidator < EachValidator
|
6
|
+
def validate_each(record, attribute, value)
|
7
|
+
return if value.blank? && options[:allow_blank]
|
8
|
+
return if value.nil? && options[:allow_nil]
|
9
|
+
return unless reserved?(value.to_s)
|
10
|
+
|
11
|
+
record.errors.add(
|
12
|
+
attribute,
|
13
|
+
:"reserved_#{options[:error_name]}",
|
14
|
+
message: options[:message],
|
15
|
+
value: value
|
16
|
+
)
|
17
|
+
end
|
18
|
+
|
19
|
+
def reserved?(subdomain)
|
20
|
+
::Validators::ReservedHostnames.reserved?(subdomain, options[:in])
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
module ClassMethods
|
25
|
+
# Validates whether or not the specified hostname is valid.
|
26
|
+
# The `in: array` can have strings and patterns. A pattern is everything
|
27
|
+
# that starts with `/` and will be parsed as a regular expression.
|
28
|
+
#
|
29
|
+
# Notice that subdomains will be normalized; it'll be downcased and have
|
30
|
+
# its underscores and hyphens stripped.
|
31
|
+
#
|
32
|
+
# class User < ActiveRecord::Base
|
33
|
+
# validates_reserved_hostname :site
|
34
|
+
#
|
35
|
+
# # Validates against a custom list.
|
36
|
+
# validates_reserved_hostname :site, in: %w[www]
|
37
|
+
# end
|
38
|
+
#
|
39
|
+
def validates_reserved_hostname(*attr_names)
|
40
|
+
options = _merge_attributes(attr_names).merge(error_name: :hostname)
|
41
|
+
validates_with ReservedHostnameValidator, options
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveModel
|
4
|
+
module Validations
|
5
|
+
class ReservedUsernameValidator < ReservedHostnameValidator
|
6
|
+
end
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
# Validates whether or not the specified username is valid.
|
10
|
+
# The `in: array` can have strings and patterns. A pattern is everything
|
11
|
+
# that starts with `/` and will be parsed as a regular expression.
|
12
|
+
#
|
13
|
+
# Notice that subdomains will be normalized; it'll be downcased and have
|
14
|
+
# its underscores and hyphens stripped.
|
15
|
+
#
|
16
|
+
# class User < ActiveRecord::Base
|
17
|
+
# validates_reserved_hostname :site
|
18
|
+
#
|
19
|
+
# # Validates against a custom list.
|
20
|
+
# validates_reserved_hostname :site, in: %w[www]
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
def validates_reserved_username(*attr_names)
|
24
|
+
options = _merge_attributes(attr_names).merge(error_name: :username)
|
25
|
+
validates_with ReservedHostnameValidator, options
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/validators/version.rb
CHANGED
data/lib/validators.rb
CHANGED
@@ -8,6 +8,8 @@ module Validators
|
|
8
8
|
require "validators/tld"
|
9
9
|
require "validators/hostname"
|
10
10
|
require "validators/disposable_hostnames"
|
11
|
+
require "validators/reserved_hostnames"
|
12
|
+
|
11
13
|
require "validators/validates_datetime"
|
12
14
|
require "validators/validates_ip_address"
|
13
15
|
require "validators/validates_email_format_of"
|
@@ -18,4 +20,8 @@ module Validators
|
|
18
20
|
require "validators/validates_ssh_private_key"
|
19
21
|
require "validators/validates_ssh_public_key"
|
20
22
|
require "validators/validates_hostname_format_of"
|
23
|
+
require "validators/validates_reserved_hostname"
|
24
|
+
require "validators/validates_reserved_username"
|
25
|
+
|
26
|
+
I18n.load_path += Dir[File.join(__dir__, "validators/locale/*.yml")]
|
21
27
|
end
|
data/test/support/models.rb
CHANGED
@@ -1,5 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
def build_model(&block)
|
4
|
+
Class.new do
|
5
|
+
include ActiveModel::Model
|
6
|
+
|
7
|
+
def self.name
|
8
|
+
"SomeModel"
|
9
|
+
end
|
10
|
+
|
11
|
+
instance_eval(&block)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
3
15
|
class User < ActiveRecord::Base
|
4
16
|
has_many :tasks
|
5
17
|
has_many :categories
|
data/test/test_helper.rb
CHANGED
@@ -1,16 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
require "simplecov-console"
|
5
|
-
|
6
|
-
SimpleCov.minimum_coverage 100
|
7
|
-
SimpleCov.minimum_coverage_by_file 100
|
8
|
-
SimpleCov.refuse_coverage_drop
|
3
|
+
$VERBOSE = nil
|
9
4
|
|
10
|
-
|
11
|
-
|
12
|
-
SimpleCov::Formatter::HTMLFormatter
|
13
|
-
])
|
5
|
+
require "simplecov"
|
6
|
+
SimpleCov.start
|
14
7
|
|
15
8
|
SimpleCov.start do
|
16
9
|
add_filter "test/support"
|
@@ -33,9 +26,6 @@ Dir[File.join(__dir__, "support/**/*.rb")].sort.each {|f| require f }
|
|
33
26
|
ActiveRecord::Base.establish_connection adapter: "sqlite3", database: ":memory:"
|
34
27
|
load "schema.rb"
|
35
28
|
|
36
|
-
I18n.enforce_available_locales = false
|
37
|
-
I18n.load_path << File.join(__dir__, "support/translations.yml")
|
38
|
-
|
39
29
|
module Minitest
|
40
30
|
class Test
|
41
31
|
setup do
|
@@ -21,7 +21,8 @@ class DisposableEmailTest < Minitest::Test
|
|
21
21
|
user = User.new(email: "user@custom.#{domain}")
|
22
22
|
user.valid?
|
23
23
|
|
24
|
-
assert_includes user.errors[:email],
|
24
|
+
assert_includes user.errors[:email],
|
25
|
+
"is not allowed (high-bounce domain)"
|
25
26
|
end
|
26
27
|
end
|
27
28
|
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "test_helper"
|
4
|
+
|
5
|
+
class ValidatesReservedHostnameTest < Minitest::Test
|
6
|
+
test "rejects reserved hostname" do
|
7
|
+
model = build_model do
|
8
|
+
attr_accessor :hostname
|
9
|
+
validates_reserved_hostname :hostname
|
10
|
+
end
|
11
|
+
|
12
|
+
instance = model.new(hostname: "www")
|
13
|
+
|
14
|
+
refute instance.valid?
|
15
|
+
assert_includes instance.errors[:hostname],
|
16
|
+
"www is a reserved hostname"
|
17
|
+
end
|
18
|
+
|
19
|
+
test "rejects reserved hostname with pattern" do
|
20
|
+
model = build_model do
|
21
|
+
attr_accessor :hostname
|
22
|
+
validates_reserved_hostname :hostname
|
23
|
+
end
|
24
|
+
|
25
|
+
instance = model.new(hostname: "www1234")
|
26
|
+
|
27
|
+
refute instance.valid?
|
28
|
+
end
|
29
|
+
|
30
|
+
test "uses custom list" do
|
31
|
+
model = build_model do
|
32
|
+
attr_accessor :hostname
|
33
|
+
validates_reserved_hostname :hostname, in: %w[nope]
|
34
|
+
end
|
35
|
+
|
36
|
+
instance = model.new(hostname: "nope")
|
37
|
+
|
38
|
+
refute instance.valid?
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "test_helper"
|
4
|
+
|
5
|
+
class ValidatesReservedUsernameTest < Minitest::Test
|
6
|
+
test "rejects reserved username" do
|
7
|
+
model = build_model do
|
8
|
+
attr_accessor :username
|
9
|
+
validates_reserved_username :username
|
10
|
+
end
|
11
|
+
|
12
|
+
instance = model.new(username: "www")
|
13
|
+
|
14
|
+
refute instance.valid?
|
15
|
+
assert_includes instance.errors[:username],
|
16
|
+
"www is a reserved username"
|
17
|
+
end
|
18
|
+
|
19
|
+
test "rejects reserved username with pattern" do
|
20
|
+
model = build_model do
|
21
|
+
attr_accessor :username
|
22
|
+
validates_reserved_username :username
|
23
|
+
end
|
24
|
+
|
25
|
+
instance = model.new(username: "www1234")
|
26
|
+
|
27
|
+
refute instance.valid?
|
28
|
+
end
|
29
|
+
|
30
|
+
test "uses custom list" do
|
31
|
+
model = build_model do
|
32
|
+
attr_accessor :username
|
33
|
+
validates_reserved_username :username, in: %w[nope]
|
34
|
+
end
|
35
|
+
|
36
|
+
instance = model.new(username: "nope")
|
37
|
+
|
38
|
+
refute instance.valid?
|
39
|
+
end
|
40
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: validators
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.0
|
4
|
+
version: 3.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nando Vieira
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-01-
|
11
|
+
date: 2020-01-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -196,12 +196,16 @@ files:
|
|
196
196
|
- bin/sync-disposable-hostnames
|
197
197
|
- bin/sync-tld
|
198
198
|
- data/disposable.json
|
199
|
+
- data/reserved_hostnames.json
|
199
200
|
- data/tld.json
|
200
201
|
- lib/validators.rb
|
201
202
|
- lib/validators/constants.rb
|
202
203
|
- lib/validators/disposable_hostnames.rb
|
203
204
|
- lib/validators/hostname.rb
|
204
205
|
- lib/validators/ip.rb
|
206
|
+
- lib/validators/locale/en.yml
|
207
|
+
- lib/validators/locale/pt-BR.yml
|
208
|
+
- lib/validators/reserved_hostnames.rb
|
205
209
|
- lib/validators/tld.rb
|
206
210
|
- lib/validators/validates_cnpj_format_of.rb
|
207
211
|
- lib/validators/validates_cpf_format_of.rb
|
@@ -210,6 +214,8 @@ files:
|
|
210
214
|
- lib/validators/validates_hostname_format_of.rb
|
211
215
|
- lib/validators/validates_ip_address.rb
|
212
216
|
- lib/validators/validates_ownership_of.rb
|
217
|
+
- lib/validators/validates_reserved_hostname.rb
|
218
|
+
- lib/validators/validates_reserved_username.rb
|
213
219
|
- lib/validators/validates_ssh_private_key.rb
|
214
220
|
- lib/validators/validates_ssh_public_key.rb
|
215
221
|
- lib/validators/validates_url_format_of.rb
|
@@ -220,7 +226,6 @@ files:
|
|
220
226
|
- test/support/hostnames.rb
|
221
227
|
- test/support/ips.rb
|
222
228
|
- test/support/models.rb
|
223
|
-
- test/support/translations.yml
|
224
229
|
- test/support/urls.rb
|
225
230
|
- test/test_helper.rb
|
226
231
|
- test/validators/disposable_email_test.rb
|
@@ -237,6 +242,8 @@ files:
|
|
237
242
|
- test/validators/validates_ip_address/ipv6_test.rb
|
238
243
|
- test/validators/validates_ip_address_test.rb
|
239
244
|
- test/validators/validates_ownership_of_test.rb
|
245
|
+
- test/validators/validates_reserved_hostname_test.rb
|
246
|
+
- test/validators/validates_reserved_username_test.rb
|
240
247
|
- test/validators/validates_ssh_private_key/bits_test.rb
|
241
248
|
- test/validators/validates_ssh_private_key/common_test.rb
|
242
249
|
- test/validators/validates_ssh_private_key/dsa_test.rb
|
@@ -275,7 +282,6 @@ test_files:
|
|
275
282
|
- test/support/hostnames.rb
|
276
283
|
- test/support/ips.rb
|
277
284
|
- test/support/models.rb
|
278
|
-
- test/support/translations.yml
|
279
285
|
- test/support/urls.rb
|
280
286
|
- test/test_helper.rb
|
281
287
|
- test/validators/disposable_email_test.rb
|
@@ -292,6 +298,8 @@ test_files:
|
|
292
298
|
- test/validators/validates_ip_address/ipv6_test.rb
|
293
299
|
- test/validators/validates_ip_address_test.rb
|
294
300
|
- test/validators/validates_ownership_of_test.rb
|
301
|
+
- test/validators/validates_reserved_hostname_test.rb
|
302
|
+
- test/validators/validates_reserved_username_test.rb
|
295
303
|
- test/validators/validates_ssh_private_key/bits_test.rb
|
296
304
|
- test/validators/validates_ssh_private_key/common_test.rb
|
297
305
|
- test/validators/validates_ssh_private_key/dsa_test.rb
|
@@ -1,39 +0,0 @@
|
|
1
|
-
en:
|
2
|
-
activerecord: &activerecord
|
3
|
-
errors:
|
4
|
-
messages:
|
5
|
-
record_invalid: "Errors: %{errors}"
|
6
|
-
invalid_date: "is not a valid date"
|
7
|
-
invalid_date_before: "needs to be before %{date}"
|
8
|
-
invalid_date_after: "needs to be after %{date}"
|
9
|
-
invalid_email: "is not a valid address"
|
10
|
-
invalid_url: "is not a valid address"
|
11
|
-
invalid_ipv4_address: "is not a valid IPv4 address"
|
12
|
-
invalid_ipv6_address: "is not a valid IPv6 address"
|
13
|
-
invalid_address: "is not a valid IP address"
|
14
|
-
invalid_owner: "is not associated with your user"
|
15
|
-
invalid_cpf: "is not a valid CPF"
|
16
|
-
invalid_cnpj: "is not a valid CNPJ"
|
17
|
-
disposable_email: "is not allowed (high-bounce domain)"
|
18
|
-
invalid_hostname: "does not have a valid hostname"
|
19
|
-
|
20
|
-
activemodel:
|
21
|
-
<<: *activerecord
|
22
|
-
|
23
|
-
pt-BR:
|
24
|
-
activerecord: &activerecord
|
25
|
-
errors:
|
26
|
-
messages:
|
27
|
-
record_invalid: "Erros: %{errors}"
|
28
|
-
invalid_email: "não parece ser um e-mail válido"
|
29
|
-
invalid_url: "não parece ser uma URL válida"
|
30
|
-
invalid_owner: "não está associado ao seu usuário"
|
31
|
-
invalid_cpf: "não é um CPF válido"
|
32
|
-
invalid_cnpj: "não é um CNPJ válido"
|
33
|
-
invalid_ssh_public_key: "não é uma chave pública de SSH válida"
|
34
|
-
invalid_ssh_private_key: "não é uma chave privada de SSH válida"
|
35
|
-
invalid_ssh_private_key_type: "precisa ser uma chave %{value}"
|
36
|
-
invalid_ssh_private_key_bits: "precisa ter pelo menos %{required} bits; a sua chave tem %{value} bits"
|
37
|
-
|
38
|
-
activemodel:
|
39
|
-
<<: *activerecord
|