Skip to content

Commit 0f0164c

Browse files
committed
fix multiple sharp signs in URIs
rename HttpUrlValidator as UriValidator
1 parent 3ac001b commit 0f0164c

File tree

4 files changed

+63
-52
lines changed

4 files changed

+63
-52
lines changed

app/models/bookmark.rb

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26,22 +26,11 @@ class Bookmark < Content
2626
validates :title, presence: { message: "Le titre est obligatoire" },
2727
length: { maximum: 100, message: "Le titre est trop long" }
2828
validates :link, presence: { message: "Vous ne pouvez pas poster un lien vide" },
29-
http_url: { message: "Le lien n'est pas valide" },
29+
uri: { message: "Le lien n'est pas valide" },
3030
length: { maximum: 255, message: "Le lien est trop long" }
3131

32-
def link=(raw)
33-
raw.strip!
34-
return write_attribute :url, nil if raw.blank?
35-
uri = URI.parse(raw)
36-
# Default to HTTP link if neither scheme nor host is found
37-
if uri.scheme.blank? && uri.host.blank?
38-
raw = "http://#{raw}"
39-
uri = URI.parse(raw)
40-
end
41-
write_attribute :link, uri.to_s
42-
# Let raw value if error when parsed, HttpUrlValidator will manage it
43-
rescue URI::InvalidURIError
44-
write_attribute :link, raw
32+
before_validation do |bookmark|
33+
bookmark.link = UriValidator.before_validation(bookmark.link);
4534
end
4635

4736
def create_node(attrs={})

app/models/link.rb

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,22 +26,12 @@ class Link < ActiveRecord::Base
2626

2727
validates :title, presence: { message: "Un lien doit obligatoirement avoir un titre" },
2828
length: { maximum: 100, message: "Le titre est trop long" }
29-
validates :url, http_url: { protocols: PROTOCOLS, message: "L'adresse n'est pas valide" },
29+
validates :url, uri: { protocols: PROTOCOLS, message: "L'adresse n'est pas valide" },
3030
presence: { message: "Un lien doit obligatoirement avoir une adresse" },
3131
length: { maximum: 255, message: "L’adresse est trop longue" }
3232

33-
def url=(raw)
34-
raw.strip!
35-
return write_attribute :url, nil if raw.blank?
36-
uri = URI.parse(raw)
37-
if uri.scheme.blank? && uri.host.blank?
38-
raw = "http://#{raw}"
39-
uri = URI.parse(raw)
40-
end
41-
write_attribute :url, uri.to_s
42-
# Let raw value if error when parsed, HttpUrlValidator will manage it
43-
rescue URI::InvalidURIError
44-
write_attribute :url, raw
33+
before_validation do |link|
34+
link.url = UriValidator.before_validation(link.url);
4535
end
4636

4737
### Behaviour ###

app/validators/http_url_validator.rb

Lines changed: 0 additions & 25 deletions
This file was deleted.

app/validators/uri_validator.rb

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# Validate if a value is a URI
2+
class UriValidator < ActiveModel::EachValidator
3+
def validate_each(record, attribute, value)
4+
if value.present? && not(valid?(value, options))
5+
record.errors.add attribute, (options[:message] || "n'est pas un lien valide")
6+
end
7+
end
8+
9+
private
10+
11+
def valid?(value, options)
12+
# Valid links can be parsed by URI
13+
uri = URI.parse(value)
14+
15+
# Authorize only given protocol
16+
if options.has_key?(:protocols)
17+
return options[:protocols].include?(uri.scheme)
18+
end
19+
20+
# Links starting with "//MY_DOMAIN" are current scheme dependent and are valid
21+
return true if uri.scheme.nil? && uri.host == MY_DOMAIN
22+
23+
# All other links are valid only if scheme and host exists
24+
return uri.scheme.present? && uri.host.present?
25+
rescue URI::InvalidURIError
26+
false
27+
end
28+
29+
def self.before_validation(raw, default_scheme='http://')
30+
raw.strip!
31+
return nil if raw.blank?
32+
33+
# Automatically encodes sharp signs (#) found in URI fragment:
34+
# RFC 3986 uses sharp to define URI fragment and requires other sharps
35+
# to be percent encoded
36+
fragments = raw.split("#")
37+
if (fragments.length > 2)
38+
raw = fragments[0] + '#' + fragments[1..-1].join('%23')
39+
end
40+
41+
uri = URI.parse(raw)
42+
43+
# If user forgot to add scheme, add default
44+
if default_scheme.present? && uri.scheme.blank? && uri.host.blank?
45+
raw = "#{default_scheme}#{raw}"
46+
uri = URI.parse(raw)
47+
end
48+
49+
return uri.to_s
50+
51+
# Let raw value if error occured when we tried to parse it, because
52+
# the HttpUrlValidator will manage it itself on validation
53+
rescue URI::InvalidURIError
54+
return raw
55+
end
56+
57+
end

0 commit comments

Comments
 (0)