url_store 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|