url_store 0.1.0 → 0.2.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.
- data/README.markdown +15 -2
- data/VERSION +1 -1
- data/lib/url_store/compact_encoder.rb +62 -0
- data/lib/url_store.rb +20 -13
- data/spec/url_store/compact_encoder_spec.rb +31 -0
- data/spec/url_store_spec.rb +25 -1
- data/url_store.gemspec +5 -2
- metadata +4 -1
data/README.markdown
CHANGED
@@ -1,15 +1,23 @@
|
|
1
1
|
Data securely stored in urls.
|
2
2
|
|
3
|
+
- url-save output
|
4
|
+
- short codes through GZip
|
5
|
+
- serializing through __:marshal__ :yaml
|
6
|
+
- hashing through DSS DSS1 MD2 MD4 MD5 MDC2 RIPEMD160 SHA __SHA1__ SHA224 SHA256 SHA384 SHA512
|
7
|
+
|
8
|
+
|
3
9
|
Install
|
4
10
|
=======
|
5
11
|
- As gem: ` sudo gem install url_store `
|
6
12
|
- As Rails plugin: ` script/plugin install git://github.com/grosser/url_store.git `
|
7
13
|
|
8
|
-
|
9
14
|
Usage
|
10
15
|
=====
|
16
|
+
# config (e.g environment.rb)
|
17
|
+
UrlStore.secret = 'adadasd2adsdasd4ads4eas4dea4dsea4sd'
|
18
|
+
|
11
19
|
# View:
|
12
|
-
<%= link_to 'paid', :controller=>:payments, :action=>:paid, :data=>UrlStore.encode(:id=>1, :status=>'paid')%>
|
20
|
+
<%= link_to 'paid', :controller=>:payments, :action=>:paid, :data=>UrlStore.encode(:id=>1, :status=>'paid') %>
|
13
21
|
|
14
22
|
# Controller:
|
15
23
|
if data = UrlStore.decode(params[:data])
|
@@ -18,6 +26,11 @@ Usage
|
|
18
26
|
raise 'FRAUD!'
|
19
27
|
end
|
20
28
|
|
29
|
+
### Options
|
30
|
+
UrlStore.secret = 'something random'
|
31
|
+
UrlStore.hasher = 'MD5' # default: 'SHA1'
|
32
|
+
UrlStore.serializer = :yaml # default: :marshal
|
33
|
+
|
21
34
|
Author
|
22
35
|
=======
|
23
36
|
[Michael Grosser](http://pragmatig.wordpress.com)
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'zlib'
|
3
|
+
|
4
|
+
class UrlStore
|
5
|
+
class CompactEncoder
|
6
|
+
def initialize(secret, options={})
|
7
|
+
@secret = secret
|
8
|
+
@hasher = options[:hasher] || 'SHA1'
|
9
|
+
@serializer = options[:serializer] || :marshal
|
10
|
+
end
|
11
|
+
|
12
|
+
def encode(data)
|
13
|
+
data = compress(serialize(data))
|
14
|
+
data+digest(data)
|
15
|
+
end
|
16
|
+
|
17
|
+
def decode(data)
|
18
|
+
hash = data[-hash_length..-1]
|
19
|
+
data = data[0...-hash_length]
|
20
|
+
|
21
|
+
if digest(data) == hash
|
22
|
+
deserialize extract(data)
|
23
|
+
else
|
24
|
+
nil
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def serialize(data)
|
31
|
+
case @serializer.to_sym
|
32
|
+
when :yaml then data.to_yaml
|
33
|
+
when :marshal then Marshal.dump(data)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def deserialize(data)
|
38
|
+
case @serializer.to_sym
|
39
|
+
when :yaml then YAML.load(data)
|
40
|
+
when :marshal then Marshal.load(data)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def compress(data)
|
45
|
+
Base64.encode64( Zlib::Deflate.deflate(data)).gsub("\n",'')
|
46
|
+
end
|
47
|
+
|
48
|
+
def extract(data)
|
49
|
+
Zlib::Inflate.inflate Base64.decode64(data)
|
50
|
+
end
|
51
|
+
|
52
|
+
def hash_length
|
53
|
+
digest('x').size
|
54
|
+
end
|
55
|
+
|
56
|
+
# stolen from ActiveSupport
|
57
|
+
def digest(data)
|
58
|
+
require 'openssl' unless defined?(OpenSSL)
|
59
|
+
OpenSSL::HMAC.hexdigest(OpenSSL::Digest::Digest.new(@hasher.to_s), @secret, data)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/lib/url_store.rb
CHANGED
@@ -1,37 +1,44 @@
|
|
1
|
-
require '
|
1
|
+
require 'url_store/compact_encoder'
|
2
2
|
|
3
3
|
class UrlStore
|
4
4
|
VERSION = File.read( File.join(File.dirname(__FILE__),'..','VERSION') ).strip
|
5
5
|
SECRET = 'asdkasjlwqjdqaccxnjkasdfh2313'
|
6
|
-
|
6
|
+
HASHER = 'SHA1'
|
7
|
+
SERIALIZER = :marshal
|
7
8
|
|
8
9
|
# (convert to base64url <-> RFC4648) and '|'
|
9
10
|
# which is not url-safe if you ask ERB/CGI, but browsers accept it
|
10
11
|
IN = '+/='
|
11
12
|
OUT = '-_|'
|
12
13
|
|
13
|
-
|
14
|
-
self.secret =
|
14
|
+
@@secret = SECRET
|
15
|
+
def self.secret=(x); @@secret=x; end
|
16
|
+
def self.secret; @@secret; end
|
17
|
+
|
18
|
+
@@hasher = HASHER
|
19
|
+
def self.hasher=(x); @@hasher=x; end
|
20
|
+
def self.hasher; @@hasher; end
|
21
|
+
|
22
|
+
@@serializer = SERIALIZER
|
23
|
+
def self.serializer=(x); @@serializer=x; end
|
24
|
+
def self.serializer; @@serializer; end
|
15
25
|
|
16
26
|
def self.encode(data)
|
17
|
-
string = encoder.
|
18
|
-
string = string.sub('--', ';') # seperator of verifier
|
27
|
+
string = encoder.encode(data)
|
19
28
|
string.to_s.tr(IN,OUT)
|
20
29
|
end
|
21
30
|
|
22
31
|
def self.decode(string)
|
23
32
|
string = string.to_s.tr(OUT,IN) # convert to base64url <-> RFC4648
|
24
|
-
|
25
|
-
begin
|
26
|
-
encoder.verify(string)
|
27
|
-
rescue ActiveSupport::MessageVerifier::InvalidSignature
|
28
|
-
nil
|
29
|
-
end
|
33
|
+
encoder.decode(string)
|
30
34
|
end
|
31
35
|
|
32
36
|
private
|
33
37
|
|
34
38
|
def self.encoder
|
35
|
-
|
39
|
+
if secret == SECRET
|
40
|
+
warn "WARNING: you should not use the default secret! use UrlStore.secret='something'"
|
41
|
+
end
|
42
|
+
UrlStore::CompactEncoder.new(secret, :hasher => hasher, :serializer => serializer)
|
36
43
|
end
|
37
44
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
describe UrlStore::CompactEncoder do
|
4
|
+
before do
|
5
|
+
@encoder = UrlStore::CompactEncoder.new('asdasdsa')
|
6
|
+
@data = {:x => 1, 'asdadadadas' => 'asdasdadawvxcxcxcvjs', 'dasdasdadsadad' => 'asdasdwxczvvcjjkdfjkdf'}
|
7
|
+
end
|
8
|
+
|
9
|
+
it "generates same code for same data" do
|
10
|
+
@encoder.encode(@data).should == @encoder.encode(@data)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "can decode / encode" do
|
14
|
+
@encoder.decode(@encoder.encode(@data)).should == @data
|
15
|
+
end
|
16
|
+
|
17
|
+
it "generates shorter codes than pure base64" do
|
18
|
+
hash_length = 40
|
19
|
+
@encoder.encode(@data).size.should < (Base64.encode64(Marshal.dump(@data)).size + hash_length)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "can encode/decode with yaml" do
|
23
|
+
@encoder = UrlStore::CompactEncoder.new('asdasdsa', :serializer => :yaml)
|
24
|
+
@encoder.decode(@encoder.encode(@data)).should == @data
|
25
|
+
end
|
26
|
+
|
27
|
+
it "can hash with other hasher" do
|
28
|
+
@encoder = UrlStore::CompactEncoder.new('asdasdsa', :hasher => 'MD5')
|
29
|
+
@encoder.decode(@encoder.encode(@data)).should == @data
|
30
|
+
end
|
31
|
+
end
|
data/spec/url_store_spec.rb
CHANGED
@@ -3,6 +3,7 @@ require 'cgi'
|
|
3
3
|
|
4
4
|
describe UrlStore do
|
5
5
|
before do
|
6
|
+
UrlStore.secret = 'not the standart sssecrettt1231231áßðáïíœï©óáßïáöððííïö'
|
6
7
|
@data = {:x => 11212, :y => 'asdasda sdasdasdASDJKSAJDLSKDLKDS', 'asdasd' => 12312312, 12.12 => 123123212312123, :asdasdasd => '2134 adasdasóáößðóöáåöäóðᜩöóöfóöåäfóöéåfó'}
|
7
8
|
end
|
8
9
|
|
@@ -20,7 +21,7 @@ describe UrlStore do
|
|
20
21
|
end
|
21
22
|
|
22
23
|
it "uses a lot of different chars" do
|
23
|
-
UrlStore.encode(@data).split('').uniq.size.should >=
|
24
|
+
UrlStore.encode(@data).split('').uniq.size.should >= 62
|
24
25
|
end
|
25
26
|
|
26
27
|
it "uses url-save characters" do
|
@@ -34,6 +35,29 @@ describe UrlStore do
|
|
34
35
|
UrlStore.decode(encoded).should == nil
|
35
36
|
end
|
36
37
|
|
38
|
+
it "warns when default secret is used" do
|
39
|
+
UrlStore.secret = UrlStore::SECRET
|
40
|
+
UrlStore.should_receive(:warn)
|
41
|
+
UrlStore.encode(1)
|
42
|
+
end
|
43
|
+
|
44
|
+
it "can compress" do
|
45
|
+
x = 'a'*100
|
46
|
+
UrlStore.encode(x).size.should <= x.size
|
47
|
+
end
|
48
|
+
|
49
|
+
it "can serialize using a different method" do
|
50
|
+
old = UrlStore.encode(@data)
|
51
|
+
UrlStore.serializer = :yaml
|
52
|
+
UrlStore.encode(@data).size.should_not == old.size
|
53
|
+
end
|
54
|
+
|
55
|
+
it "can serialize using different hasher" do
|
56
|
+
old = UrlStore.encode(@data)
|
57
|
+
UrlStore.hasher = 'MD5'
|
58
|
+
UrlStore.encode(@data).size.should_not == old.size
|
59
|
+
end
|
60
|
+
|
37
61
|
it "has a VERSION" do
|
38
62
|
UrlStore::VERSION.should =~ /^\d+\.\d+\.\d+$/
|
39
63
|
end
|
data/url_store.gemspec
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{url_store}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.2.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Michael Grosser"]
|
@@ -20,7 +20,9 @@ Gem::Specification.new do |s|
|
|
20
20
|
"Rakefile",
|
21
21
|
"VERSION",
|
22
22
|
"lib/url_store.rb",
|
23
|
+
"lib/url_store/compact_encoder.rb",
|
23
24
|
"spec/spec_helper.rb",
|
25
|
+
"spec/url_store/compact_encoder_spec.rb",
|
24
26
|
"spec/url_store_spec.rb",
|
25
27
|
"url_store.gemspec"
|
26
28
|
]
|
@@ -31,7 +33,8 @@ Gem::Specification.new do |s|
|
|
31
33
|
s.summary = %q{Data securely stored in urls.}
|
32
34
|
s.test_files = [
|
33
35
|
"spec/spec_helper.rb",
|
34
|
-
"spec/url_store_spec.rb"
|
36
|
+
"spec/url_store_spec.rb",
|
37
|
+
"spec/url_store/compact_encoder_spec.rb"
|
35
38
|
]
|
36
39
|
|
37
40
|
if s.respond_to? :specification_version then
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: url_store
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Grosser
|
@@ -26,7 +26,9 @@ files:
|
|
26
26
|
- Rakefile
|
27
27
|
- VERSION
|
28
28
|
- lib/url_store.rb
|
29
|
+
- lib/url_store/compact_encoder.rb
|
29
30
|
- spec/spec_helper.rb
|
31
|
+
- spec/url_store/compact_encoder_spec.rb
|
30
32
|
- spec/url_store_spec.rb
|
31
33
|
- url_store.gemspec
|
32
34
|
has_rdoc: true
|
@@ -60,3 +62,4 @@ summary: Data securely stored in urls.
|
|
60
62
|
test_files:
|
61
63
|
- spec/spec_helper.rb
|
62
64
|
- spec/url_store_spec.rb
|
65
|
+
- spec/url_store/compact_encoder_spec.rb
|