tool_pouch 0.0.34
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 +7 -0
- data/Gemfile +5 -0
- data/README +22 -0
- data/Rakefile +3 -0
- data/data/iso639.yml +412 -0
- data/lib/tool_pouch.rb +43 -0
- data/lib/tool_pouch/babel.rb +225 -0
- data/lib/tool_pouch/krono.rb +153 -0
- data/lib/tool_pouch/locksmith.rb +111 -0
- data/lib/tool_pouch/mathema.rb +27 -0
- data/lib/tool_pouch/trail.rb +279 -0
- data/lib/tool_pouch/transistor.rb +98 -0
- data/lib/tool_pouch/version.rb +3 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/tool_pouch/krono_spec.rb +12 -0
- data/spec/tool_pouch/locksmith/digest_spec.rb +105 -0
- metadata +88 -0
data/lib/tool_pouch.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'cgi'
|
3
|
+
require 'digest/sha1'
|
4
|
+
require 'digest/md5'
|
5
|
+
require 'find'
|
6
|
+
require 'net/http'
|
7
|
+
require 'openssl'
|
8
|
+
require 'yaml'
|
9
|
+
require 'uri'
|
10
|
+
# HashTree
|
11
|
+
|
12
|
+
module ToolPouch
|
13
|
+
DATA_PATH = File.expand_path("../../data", __FILE__) + '/'
|
14
|
+
|
15
|
+
def self.get_data(name, extension='yml')
|
16
|
+
set_data_cache(name, YAML.load_file("#{DATA_PATH}#{name}.#{extension}")['data']) if not data_cached?(name)
|
17
|
+
|
18
|
+
return get_data_cache(name)
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.get_data_cache(name)
|
22
|
+
@data_cache[name]
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.set_data_cache(name, data)
|
26
|
+
@data_cache = {} if not @data_cache
|
27
|
+
|
28
|
+
@data_cache[name] = data
|
29
|
+
|
30
|
+
return @data_cache[name]
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.data_cached?(name)
|
34
|
+
@data_cache and @data_cache[name]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
require 'tool_pouch/babel'
|
39
|
+
require 'tool_pouch/krono'
|
40
|
+
require 'tool_pouch/locksmith'
|
41
|
+
require 'tool_pouch/mathema'
|
42
|
+
require 'tool_pouch/trail'
|
43
|
+
require 'tool_pouch/transistor'
|
@@ -0,0 +1,225 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
module ToolPouch::Babel
|
3
|
+
def self.bytes_to(size, options={})
|
4
|
+
options.reverse_merge!(:unit => nil, :show_unit => true)
|
5
|
+
|
6
|
+
unit = options[:unit]
|
7
|
+
|
8
|
+
divider = 1
|
9
|
+
|
10
|
+
unless options[:unit]
|
11
|
+
if size < 1000
|
12
|
+
unit = :b
|
13
|
+
elsif size < 1000000
|
14
|
+
unit = :kb
|
15
|
+
elsif size < 1000000000
|
16
|
+
unit = :mb
|
17
|
+
else
|
18
|
+
unit = :gb
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
case unit
|
23
|
+
when :kb then divider = 1000
|
24
|
+
when :mb then divider = 1000000
|
25
|
+
when :gb then divider = 1000000000
|
26
|
+
end
|
27
|
+
|
28
|
+
sprintf("%0.02f", (size.to_f / divider)) + (options[:show_unit] ? ' ' + unit.to_s.upcase_utf8 : '')
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.convert_accents(str)
|
32
|
+
str_to_convert = String.new(str)
|
33
|
+
table = {'Á' => 'A', 'Â' => 'A', 'Ä' => 'A', 'À' => 'A', 'Å' => 'A', 'Ç' => 'C', 'É' => 'E', 'Ê' => 'E', 'Ë' => 'E', 'È' => 'E', 'Í' => 'I', 'Î' => 'I', 'Ï' => 'I', 'Ì' => 'I', 'Ñ' => 'N', 'Œ' => 'O', 'Ó' => 'O', 'Ô' => 'O', 'Ö' => 'O', 'Ò' => 'O', 'Õ' => 'O', 'Ú' => 'U', 'Û' => 'U', 'Ü' => 'U', 'Ù' => 'U', 'Ý' => 'Y', 'Ÿ' => 'Y', 'Ž' => 'Z', 'á' => 'a', 'â' => 'a', 'ä' => 'a', 'æ' => 'a', 'à' => 'a', 'å' => 'a', 'ã' => 'a', 'ç' => 'c', 'é' => 'e', 'ê' => 'e', 'ë' => 'e', 'è' => 'e', 'ð' => 'o', 'í' => 'i', 'î' => 'i', 'ï' => 'i', 'ì' => 'i', 'ñ' => 'n', 'ó' => 'o', 'ô' => 'o', 'ö' => 'o', 'œ' => 'o', 'ò' => 'o', 'õ' => 'o', 'ú' => 'u', 'û' => 'u', 'ü' => 'u', 'ù' => 'u', 'ý' => 'y', 'ÿ' => 'y', 'ž' => 'z'}
|
34
|
+
|
35
|
+
table.each { |k, v| str_to_convert.gsub!(k, v) }
|
36
|
+
|
37
|
+
return str_to_convert
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.currency_for_country(country)
|
41
|
+
case country
|
42
|
+
when 'fra', 'ita', 'lux', 'bel', 'deu' then 'eur'
|
43
|
+
when 'can' then 'cad'
|
44
|
+
when 'usa' then 'usd'
|
45
|
+
when 'che' then 'chf'
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.currency_for_region(region)
|
50
|
+
return case region
|
51
|
+
when 'europe' then 'eur'
|
52
|
+
when 'quebec' then 'cad'
|
53
|
+
when 'usa' then 'usd'
|
54
|
+
when 'suisse' then 'chf'
|
55
|
+
else ''
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.enumerate(terms)
|
60
|
+
last_element = terms.pop;
|
61
|
+
|
62
|
+
if terms.length == 0
|
63
|
+
return last_element;
|
64
|
+
else
|
65
|
+
return terms.join(', ') + ' ' + I18n.t('and') + ' ' + last_element;
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.extract_first_quotes(value)
|
70
|
+
value.index('"') ? value.split('"')[1] : ''
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.extract_first_tag(value)
|
74
|
+
if value.index('<') and value.index('>')
|
75
|
+
return value[value.index('<') + 2, value.rindex('>') - value.index('<')-2]
|
76
|
+
else
|
77
|
+
return ''
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.random_string(size, options={})
|
82
|
+
options.reverse_merge!(:force_array => false, :quantity => 1, :reject => [])
|
83
|
+
|
84
|
+
chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
|
85
|
+
strings = []
|
86
|
+
try = 0
|
87
|
+
|
88
|
+
options[:quantity].times do
|
89
|
+
new_random = ''
|
90
|
+
while new_random.empty? do
|
91
|
+
1.upto(size) { |i| new_random << chars[rand(chars.size)] }
|
92
|
+
|
93
|
+
new_random = '' if options[:reject].include?(new_random)
|
94
|
+
try += 1
|
95
|
+
|
96
|
+
return nil if try > 500
|
97
|
+
end
|
98
|
+
options[:reject] << new_random
|
99
|
+
strings << new_random
|
100
|
+
end
|
101
|
+
|
102
|
+
(options[:quantity] == 1 and not options[:force_array]) ? strings.first : strings
|
103
|
+
end
|
104
|
+
|
105
|
+
def self.region_for_country(country)
|
106
|
+
return case country.downcase
|
107
|
+
when 'ita', 'fra', 'lux', 'bel', 'deu' then 'europe'
|
108
|
+
when 'che' then 'suisse'
|
109
|
+
when 'can' then 'quebec'
|
110
|
+
when 'usa' then 'usa'
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def self.strip_html(value)
|
115
|
+
(value) ? value.to_s.gsub(/<\/?[^>]*>/, "") : value
|
116
|
+
end
|
117
|
+
|
118
|
+
def self.to_currency_symbol(unit)
|
119
|
+
return case unit.downcase
|
120
|
+
when 'cad', 'usd' then '$'
|
121
|
+
when 'eur' then '€'
|
122
|
+
when 'chf' then 'CHF'
|
123
|
+
else ''
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
# TODO : Finish the method
|
128
|
+
def self.to_iso3166_1(country_code, options={})
|
129
|
+
options.reverse_merge!(:to_alpha => '3')
|
130
|
+
|
131
|
+
from_2_to_3 = { "be" => "bel", "ca" => "can", "ch" => "che", "de" => "deu", "fr" => "fra", "it" => "ita", "lx" => "lux", "us" => "usa" }
|
132
|
+
from_3_to_2 = from_2_to_3.invert
|
133
|
+
|
134
|
+
if options[:to_alpha].to_s == '3'
|
135
|
+
return from_2_to_3[country_code.to_s.downcase]
|
136
|
+
elsif options[:to_alpha].to_s == '2'
|
137
|
+
return from_3_to_2[country_code.to_s.downcase]
|
138
|
+
else
|
139
|
+
return nil
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def self.to_iso639(language, options={})
|
144
|
+
options.reverse_merge!(:from => nil, :to => '2b')
|
145
|
+
|
146
|
+
options[:from] = options[:from] ? [options[:from]].flatten : ['1', '2b', '2t', '3']
|
147
|
+
|
148
|
+
options[:from].each do |from|
|
149
|
+
table = ToolPouch.get_data('iso639')['iso639_convert']["#{from}_to_#{options[:to]}"]
|
150
|
+
|
151
|
+
return table[language] if table and not table[language].to_s.empty?
|
152
|
+
end
|
153
|
+
|
154
|
+
return language
|
155
|
+
end
|
156
|
+
|
157
|
+
def self.to_latin_encoded(value)
|
158
|
+
latin_table = { 'Æ' => '306', 'Á' => '301', 'Â' => '302', 'Ä' => '304', 'À' => '300', 'Å' => '305', 'Ã' => '303', 'Ç' => '307', 'É' => '311', 'Ê' => '312', 'Ë' => '313', 'È' => '310', 'Ð' => '320', '€' => '240', 'Í' => '315', 'Î' => '316', 'Ï' => '317', 'Ì' => '314', 'Ł' => '225', 'Ñ' => '321', 'Œ' => '226', 'Ó' => '323', 'Ô' => '324', 'Ö' => '326', 'Ò' => '322', 'Ø' => '330', 'Õ' => '325', 'Š' => '227', 'Þ' => '336', 'Ú' => '332', 'Û' => '333', 'Ü' => '334', 'Ù' => '331', 'Ý' => '335', 'Ÿ' => '230', 'Ž' => '231', 'á' => '341', 'â' => '342', '´' => '264', 'ä' => '344', 'æ' => '346', 'à' => '340', 'å' => '345', '^' => '136', '~' => '176', '*' => '052', 'ã' => '343', '˘' => '030', '¦' => '246', '•' => '200', 'ˇ' => '031', 'ç' => '347', '¸' => '270', '¢' => '242', 'ˆ' => '032', '©' => '251', '¤' => '244', '†' => '201', '‡' => '202', '°' => '260', '÷' => '367', 'ı' => '232', 'é' => '351', 'ê' => '352', 'ë' => '353', 'è' => '350', 'ð' => '360', '¡' => '241', 'fi' => '223', 'fl' => '224', 'ƒ' => '206', 'ß' => '337', '«' => '253', '»' => '273', 'í' => '355', 'î' => '356', 'ï' => '357', 'ì' => '354', 'ł' => '233', 'µ' => '265', '×' => '327', 'ñ' => '361', '#' => '043', 'ó' => '363', 'ô' => '364', 'ö' => '366', 'œ' => '234', 'ò' => '362', 'ª' => '252', 'º' => '272', 'ø' => '370', 'õ' => '365', '¶' => '266', '¿' => '277', '„' => '214', '“' => '215', '”' => '216', '®' => '256', 'š' => '235', '§' => '247', '£' => '243', 'þ' => '376', '™' => '222', 'ú' => '372', 'û' => '373', 'ü' => '374', 'ù' => '371', 'ý' => '375', 'ÿ' => '377', '¥' => '245', 'ž' => '236' }
|
159
|
+
value_encoded = ''
|
160
|
+
|
161
|
+
value.each_char { |c| value_encoded << (latin_table[c] ? "\\" + latin_table[c] : c) }
|
162
|
+
|
163
|
+
value_encoded
|
164
|
+
end
|
165
|
+
|
166
|
+
def self.to_price(amount, options={})
|
167
|
+
options.reverse_merge!(:separator => ',', :format => '%n%u', :unit => 'cad')
|
168
|
+
|
169
|
+
price = amount.to_s.rjust(3,'0')
|
170
|
+
price = price[0..-3] + options[:separator] + price[-2..-1]
|
171
|
+
|
172
|
+
if options[:unit]
|
173
|
+
unit_symbol = to_currency_symbol(options[:unit])
|
174
|
+
price = options[:format].gsub('%u', unit_symbol).gsub('%n', price)
|
175
|
+
end
|
176
|
+
|
177
|
+
return price
|
178
|
+
end
|
179
|
+
|
180
|
+
def self.to_roman(numeric)
|
181
|
+
numerals = [
|
182
|
+
{ :roman => 'M', :arab => 1000 },
|
183
|
+
{ :roman => 'CM', :arab => 900 },
|
184
|
+
{ :roman => 'D', :arab => 500 },
|
185
|
+
{ :roman => 'CD', :arab => 400 },
|
186
|
+
{ :roman => 'C', :arab => 100 },
|
187
|
+
{ :roman => 'XC', :arab => 90 },
|
188
|
+
{ :roman => 'L', :arab => 50 },
|
189
|
+
{ :roman => 'XL', :arab => 40 },
|
190
|
+
{ :roman => 'X', :arab => 10 },
|
191
|
+
{ :roman => 'IX', :arab => 9 },
|
192
|
+
{ :roman => 'V', :arab => 5 },
|
193
|
+
{ :roman => 'IV', :arab => 4 },
|
194
|
+
{ :roman => 'I', :arab => 1 }
|
195
|
+
]
|
196
|
+
|
197
|
+
roman = '';
|
198
|
+
numerals.each do |num|
|
199
|
+
while numeric >= num[:arab]
|
200
|
+
roman += num[:roman]
|
201
|
+
numeric -= num[:arab]
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
return roman
|
206
|
+
end
|
207
|
+
|
208
|
+
def self.to_slug(value)
|
209
|
+
value.mb_chars.normalize(:kd).gsub(/[^\x00-\x7F]/n,'').downcase.gsub(/[^[:alnum:]]/,'-')
|
210
|
+
end
|
211
|
+
|
212
|
+
def self.to_slug_advanced(value)
|
213
|
+
to_slug(value).gsub(/-{2,}/,'-').gsub(/-\Z/, '')
|
214
|
+
end
|
215
|
+
|
216
|
+
def self.validate_email?(email, options={})
|
217
|
+
options.reverse_merge!(:allow_blank => false)
|
218
|
+
|
219
|
+
if options[:allow_blank] and email.blank?
|
220
|
+
return true
|
221
|
+
else
|
222
|
+
return (email.match(/^\S+@\S+\.\S+$/) ? true : false)
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
@@ -0,0 +1,153 @@
|
|
1
|
+
module ToolPouch::Krono
|
2
|
+
def self.datestamp(date=nil)
|
3
|
+
date = Time.now unless date
|
4
|
+
|
5
|
+
return date.strftime("%Y%m%d")
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.floor(datetime, seconds=60)
|
9
|
+
return Time.at((datetime.to_f / seconds).floor * seconds)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.format_in_time(number)
|
13
|
+
minutes = (number >= 1) ? number.modulo(number.floor) : number
|
14
|
+
minutes = (minutes * 60).to_i.to_s
|
15
|
+
|
16
|
+
return number.floor.to_s.rjust(2, '0') + ':' + minutes.rjust(2, '0')
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.is_iso8601?(date)
|
20
|
+
date.is_a? String and date =~ /^\d{4}-(0\d|1[0-2])-([0-2]\d|3[0-1])T([0-1]\d|2[0-4]):([0-5]\d):([0-5]\d)([zZ]|([+-](0\d|1[0-4]):[0-5]\d))/
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.is_valid?(strtime, options={})
|
24
|
+
options.reverse_merge!(:blank_is_valid => true)
|
25
|
+
|
26
|
+
if strtime.blank?
|
27
|
+
return options[:blank_is_valid]
|
28
|
+
else
|
29
|
+
begin
|
30
|
+
return (Time.zone.parse(strtime) ? true : false)
|
31
|
+
rescue
|
32
|
+
return false
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.parse_in_utc(date_string)
|
38
|
+
last_time_zone = Time.zone.name
|
39
|
+
|
40
|
+
Time.zone = 'UTC'
|
41
|
+
utc_time = Time.zone.parse(date_string)
|
42
|
+
Time.zone = last_time_zone
|
43
|
+
|
44
|
+
return utc_time
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.parse_timestamp(raw_timestamp, options={})
|
48
|
+
options.reverse_merge!(:time_only => false)
|
49
|
+
|
50
|
+
timestamp = Time.zone.parse(raw_timestamp)
|
51
|
+
|
52
|
+
return options[:time_only] ? timestamp.strftime("%H:%M:%S") : timestamp
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.round(datetime, seconds=60)
|
56
|
+
return Time.at((datetime.to_f / seconds).round * seconds)
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.seconds_to_unit(seconds, options={})
|
60
|
+
options.reverse_merge!(:force => nil, :format => :time, :show_unit => true)
|
61
|
+
seconds = seconds.to_f
|
62
|
+
|
63
|
+
result = seconds
|
64
|
+
time_table = {'year' => 31536000, 'day' => 86400, 'hour' => 3600, 'minute' => 60, 'second' => 1}
|
65
|
+
unit_selected = 'second'
|
66
|
+
|
67
|
+
time_table.each do |unit, value|
|
68
|
+
if (seconds >= value and not options[:force]) or (unit == options[:force])
|
69
|
+
unit_selected = unit
|
70
|
+
result = seconds / value
|
71
|
+
break
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
formatted_result = (options[:format] == :time) ? format_in_time(result) : sprintf("%0.02f", result)
|
76
|
+
|
77
|
+
if options[:show_unit]
|
78
|
+
return formatted_result + " " + I18n.t("datetime.short.#{unit_selected}")
|
79
|
+
else
|
80
|
+
return formatted_result.to_i
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.start_timer
|
85
|
+
@timer = Time.now
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.stop_timer
|
89
|
+
Time.now - @timer
|
90
|
+
end
|
91
|
+
|
92
|
+
def self.time_format
|
93
|
+
{
|
94
|
+
:basic => "%Y%m%dT%H%M%S",
|
95
|
+
:shortdate => "%Y-%m-%d",
|
96
|
+
:shorttime => "%H:%M:%S",
|
97
|
+
:shortdatetime => "%Y-%m-%dT%H:%M:%S"
|
98
|
+
}
|
99
|
+
end
|
100
|
+
|
101
|
+
def self.time_process_in_seconds(time)
|
102
|
+
time = time.split('.')[0]
|
103
|
+
new_time = time.split(':')
|
104
|
+
|
105
|
+
return (new_time[new_time.length-2].to_i * 60).to_i + new_time.last.to_i
|
106
|
+
end
|
107
|
+
|
108
|
+
def self.datetime_standard
|
109
|
+
[ :iso8601 ]
|
110
|
+
end
|
111
|
+
|
112
|
+
def self.timestamp(date=nil)
|
113
|
+
date = Time.now.getgm unless date
|
114
|
+
|
115
|
+
return date.strftime("%Y%m%d%H%M%S")
|
116
|
+
end
|
117
|
+
|
118
|
+
def self.timestamp_db(date=nil)
|
119
|
+
date = Time.now unless date
|
120
|
+
|
121
|
+
return date.strftime("%Y-%m-%d %H:%M:%S")
|
122
|
+
end
|
123
|
+
|
124
|
+
def self.timezone(date, options={})
|
125
|
+
options.reverse_merge!(:standard => :iso8601)
|
126
|
+
send("timezone_" + options[:standard].to_s, date) if datetime_standard.include?(options[:standard])
|
127
|
+
end
|
128
|
+
|
129
|
+
def self.timezone_iso8601(date)
|
130
|
+
if date.utc?
|
131
|
+
'Z'
|
132
|
+
else
|
133
|
+
off = date.utc_offset
|
134
|
+
sign = off < 0 ? '-' : '+'
|
135
|
+
sprintf('%s%02d:%02d', sign, *(off.abs / 60).divmod(60))
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def self.to_iso8601(date, options={})
|
140
|
+
options.reverse_merge!(:format => :basic, :utc => false, :timezone => false)
|
141
|
+
|
142
|
+
date = date.utc if options[:utc]
|
143
|
+
|
144
|
+
if time_format[options[:format]]
|
145
|
+
iso8601_date = date.strftime(time_format[options[:format]])
|
146
|
+
iso8601_date = iso8601_date + timezone(date, :standard => :iso8601) if options[:timezone] and options[:format] != :shortdate
|
147
|
+
else
|
148
|
+
iso8601_date = date.iso8601
|
149
|
+
end
|
150
|
+
|
151
|
+
return iso8601_date
|
152
|
+
end
|
153
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'active_support/time'
|
2
|
+
require 'active_support/core_ext/object'
|
3
|
+
require 'tool_pouch/trail'
|
4
|
+
|
5
|
+
module ToolPouch::Locksmith
|
6
|
+
include ToolPouch
|
7
|
+
|
8
|
+
def self.build_digest_request(url, password, lifespan = 2.minutes)
|
9
|
+
if url.blank? || password.blank?
|
10
|
+
nil
|
11
|
+
else
|
12
|
+
nonce = Babel.random_string(15)
|
13
|
+
timestamp = (Time.now + lifespan).utc.strftime('%Y-%m-%dT%H:%M:%SZ')
|
14
|
+
escaped_timestamp = CGI.escape(timestamp)
|
15
|
+
|
16
|
+
uri = Addressable::URI.parse url
|
17
|
+
params = uri.query_values || {}
|
18
|
+
params.merge!({'timestamp' => timestamp, 'nonce' => nonce})
|
19
|
+
|
20
|
+
digested_url = digest_uri(uri, params).to_s
|
21
|
+
params.merge!({
|
22
|
+
token: hexdigest([password, nonce, escaped_timestamp].join, digested_url)
|
23
|
+
})
|
24
|
+
uri.query_values = params
|
25
|
+
uri.to_s
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.check_digest_request(url, password)
|
30
|
+
if url.blank? || password.blank?
|
31
|
+
false
|
32
|
+
else
|
33
|
+
uri = Addressable::URI.parse url
|
34
|
+
params = uri.query_values || {}
|
35
|
+
|
36
|
+
unescaped_timestamp = CGI.unescape(params['timestamp'].to_s)
|
37
|
+
escaped_timestamp = CGI.escape(unescaped_timestamp)
|
38
|
+
token_key = [password, params['nonce'].to_s, escaped_timestamp].join
|
39
|
+
|
40
|
+
digested_url = digest_uri(uri, params).to_s
|
41
|
+
|
42
|
+
expire_at = Krono.parse_in_utc(unescaped_timestamp)
|
43
|
+
compare_hexdigest(token_key, digested_url, params['token']) && expire_at.future?
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Harmonize generated URL and request URL so they can be compared.
|
48
|
+
# Digest params are added to the query
|
49
|
+
# Scheme is ignored (harmonized to http)
|
50
|
+
# Note that the gem Adressable do order the query string by key values
|
51
|
+
def self.digest_uri(original_uri, params = nil)
|
52
|
+
uri = original_uri.dup
|
53
|
+
if params
|
54
|
+
p = params.dup
|
55
|
+
p.delete 'token'
|
56
|
+
uri.query_values = p
|
57
|
+
end
|
58
|
+
uri.scheme = nil
|
59
|
+
uri.port = nil
|
60
|
+
uri.to_s
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.compare_hexdigest(password, value, target_key)
|
64
|
+
password and value and OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha1'), password, value.to_s) == target_key
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.decrypt(value, password)
|
68
|
+
decrypted_value = nil
|
69
|
+
|
70
|
+
if value and password
|
71
|
+
c = OpenSSL::Cipher::Cipher.new("aes-256-cbc")
|
72
|
+
c.decrypt
|
73
|
+
c.key = Digest::SHA1.hexdigest(password)
|
74
|
+
begin
|
75
|
+
decrypted_value = c.update(Base64.decode64(value))
|
76
|
+
decrypted_value << c.final
|
77
|
+
rescue
|
78
|
+
decrypted_value = nil
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
return decrypted_value
|
83
|
+
end
|
84
|
+
|
85
|
+
def self.encrypt(value, password)
|
86
|
+
encrypt = nil
|
87
|
+
|
88
|
+
if value and password
|
89
|
+
begin
|
90
|
+
c = OpenSSL::Cipher::Cipher.new("aes-256-cbc")
|
91
|
+
c.encrypt
|
92
|
+
c.key = Digest::SHA1.hexdigest(password)
|
93
|
+
e = c.update(value)
|
94
|
+
e << c.final
|
95
|
+
encrypt = Base64.encode64(e).strip
|
96
|
+
rescue
|
97
|
+
encrypt = nil
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
return encrypt
|
102
|
+
end
|
103
|
+
|
104
|
+
def self.generate_file_checksum(file)
|
105
|
+
File.exist?(file) ? Digest::MD5.hexdigest(File.read(file)) : nil
|
106
|
+
end
|
107
|
+
|
108
|
+
def self.hexdigest(password, value)
|
109
|
+
(password and value) ? OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha1'), password, value) : nil
|
110
|
+
end
|
111
|
+
end
|