refcode 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +27 -2
- data/lib/refcode.rb +2 -36
- data/lib/refcode/encodable.rb +47 -0
- data/lib/refcode/encoder.rb +31 -0
- data/lib/refcode/version.rb +1 -1
- data/test/refcode_test.rb +39 -0
- metadata +3 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 420403fbddbdbbfe612045ab19119651a54f8740
|
4
|
+
data.tar.gz: 41307cc055dfbea7997fc7e66f20dffbcf94ff9a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b3fd63ca01f994071ac81ab8545f1880433a661806d73aa81797aeb83fa4763ea7f636777926f823b2df1115df90a3ab463c0c6a74424cceef958274d2095284
|
7
|
+
data.tar.gz: 172f2f19bc9a3d6f02c75adc665bda99f4a12f5c6edbf825e4c4845cfd42de3fc88e9da06f215fbcfa862b54daa58a640bddf6eb4d50b291988e4a11dc2c270f
|
data/README.md
CHANGED
@@ -5,7 +5,9 @@ URL-safe strings. The strings can be simply decrypted later to be used by your
|
|
5
5
|
application.
|
6
6
|
|
7
7
|
Refcode was conceived as a way to attach referral data to tracking links. Encryption
|
8
|
-
makes it unlikely that the links
|
8
|
+
makes it unlikely that the links will be tampered with. Still, it is not recommended
|
9
|
+
for critically sensitive applications! It was designed to power a simple referral
|
10
|
+
link tracking system.
|
9
11
|
|
10
12
|
## Implementation
|
11
13
|
|
@@ -31,6 +33,8 @@ Or install it yourself as:
|
|
31
33
|
|
32
34
|
## Usage
|
33
35
|
|
36
|
+
Refcode can be used in two ways. Most simply, you can use the `Refcode::Encoder` class directly.
|
37
|
+
|
34
38
|
encoder = Refcode::Encoder.new do |e|
|
35
39
|
e.secret = "a long crunchy secret"
|
36
40
|
e.salt = "some salt"
|
@@ -39,11 +43,32 @@ Or install it yourself as:
|
|
39
43
|
something = OpenStruct.new(channel: 'facebook', action: 'message', user_id: 43765)
|
40
44
|
encoded_param = encoder.encode something
|
41
45
|
|
42
|
-
# in some future request
|
46
|
+
# in some future request:
|
43
47
|
|
44
48
|
something = encoder.decode params[:encoded_param]
|
45
49
|
puts something.channel # 'facebook'
|
46
50
|
|
51
|
+
For added convenience when using Refcode with an ORM such as ActiveRecord, a module called `Refcode::Encodable` is also provided.
|
52
|
+
|
53
|
+
class Job < ActiveRecord::Base
|
54
|
+
include Refcode::Encodable
|
55
|
+
has_refcode secret: 'a long crunchy secret', salt: :id
|
56
|
+
end
|
57
|
+
|
58
|
+
# the salt option of the has_refcode method can accept a proc/lambda, string
|
59
|
+
# or a symbol representing an existing method on the class (id in this case)
|
60
|
+
|
61
|
+
# in some controller code:
|
62
|
+
|
63
|
+
@job = Job.find params[:id]
|
64
|
+
referral_code = @job.generate_refcode channel: 'facebook', action: 'message', user_id: 43765
|
65
|
+
|
66
|
+
# and in another action:
|
67
|
+
|
68
|
+
@job = Job.find params[:id]
|
69
|
+
referral_data = @job.parse_refcode params[:encoded_param]
|
70
|
+
|
71
|
+
|
47
72
|
## Contributing
|
48
73
|
|
49
74
|
1. Fork it
|
data/lib/refcode.rb
CHANGED
@@ -3,39 +3,5 @@ require 'yaml'
|
|
3
3
|
require 'base64url'
|
4
4
|
require 'encryptor'
|
5
5
|
require 'refcode/version'
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
class Encoder
|
10
|
-
|
11
|
-
attr_accessor :secret, :salt
|
12
|
-
|
13
|
-
def initialize
|
14
|
-
yield self if block_given?
|
15
|
-
end
|
16
|
-
|
17
|
-
def encode val
|
18
|
-
Base64URL.encode(encrypt(YAML.dump(val)))
|
19
|
-
end
|
20
|
-
|
21
|
-
def decode val
|
22
|
-
YAML.load(decrypt(Base64URL.decode(val)))
|
23
|
-
end
|
24
|
-
|
25
|
-
def encrypt val
|
26
|
-
Encryptor.encrypt(:value => val, :key => @secret, :salt => @salt, :iv => iv)
|
27
|
-
end
|
28
|
-
|
29
|
-
def decrypt val
|
30
|
-
Encryptor.decrypt(:value => val, :key => @secret, :salt => @salt, :iv => iv)
|
31
|
-
end
|
32
|
-
|
33
|
-
private
|
34
|
-
|
35
|
-
def iv
|
36
|
-
[@secret, @salt].join
|
37
|
-
end
|
38
|
-
|
39
|
-
end
|
40
|
-
|
41
|
-
end
|
6
|
+
require 'refcode/encoder'
|
7
|
+
require 'refcode/encodable'
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Refcode
|
2
|
+
|
3
|
+
module Encodable
|
4
|
+
|
5
|
+
def self.included base
|
6
|
+
base.extend ClassMethods
|
7
|
+
end
|
8
|
+
|
9
|
+
def generate_refcode val
|
10
|
+
raise 'You must call the has_refcode method in your class definition' unless self.class.refcode_options
|
11
|
+
encoder.encode val
|
12
|
+
end
|
13
|
+
|
14
|
+
def parse_refcode code
|
15
|
+
encoder.decode code
|
16
|
+
end
|
17
|
+
|
18
|
+
module ClassMethods
|
19
|
+
def has_refcode options
|
20
|
+
raise ArgumentError unless options[:secret] && options[:salt]
|
21
|
+
@refcode_options = options
|
22
|
+
end
|
23
|
+
def refcode_options
|
24
|
+
@refcode_options
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def encoder
|
31
|
+
Refcode::Encoder.new do |r|
|
32
|
+
r.secret = self.class.refcode_options[:secret]
|
33
|
+
salt_option = self.class.refcode_options[:salt]
|
34
|
+
if salt_option.respond_to? :call
|
35
|
+
r.salt = salt_option.call
|
36
|
+
elsif salt_option.is_a? Symbol
|
37
|
+
r.salt = self.send salt_option
|
38
|
+
else
|
39
|
+
r.salt = salt_option
|
40
|
+
end
|
41
|
+
raise "Value for salt is nil (#{salt_option} given)" if r.salt.nil?
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Refcode
|
2
|
+
|
3
|
+
class Encoder
|
4
|
+
|
5
|
+
attr_accessor :secret, :salt
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
yield self if block_given?
|
9
|
+
end
|
10
|
+
def encode val
|
11
|
+
Base64URL.encode(encrypt(YAML.dump(val)))
|
12
|
+
end
|
13
|
+
def decode val
|
14
|
+
YAML.load(decrypt(Base64URL.decode(val)))
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def encrypt val
|
20
|
+
Encryptor.encrypt(:value => val, :key => @secret, :salt => @salt, :iv => iv)
|
21
|
+
end
|
22
|
+
def decrypt val
|
23
|
+
Encryptor.decrypt(:value => val, :key => @secret, :salt => @salt, :iv => iv)
|
24
|
+
end
|
25
|
+
def iv
|
26
|
+
[@secret, @salt].join
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
data/lib/refcode/version.rb
CHANGED
data/test/refcode_test.rb
CHANGED
@@ -7,6 +7,13 @@ class RefcodeTest < Test::Unit::TestCase
|
|
7
7
|
SECRET = "686cb0461769ebed556f56bc88c956bfdf786646ab1920f58201723c557ccbaeb57112fd53302c06475e6e19a176ba617ee284b091d375858cb259e8578c620f"
|
8
8
|
SALT = "dcc74de5efdab0d80b5d763f8a63bef3d37c7453ee45446fbbccd26648af8ca6a112576dafbf5475f3cd7b968703cc9e0682ee45b8b622045cec287908f536e8"
|
9
9
|
|
10
|
+
class BareEncodableObject < OpenStruct
|
11
|
+
include Refcode::Encodable
|
12
|
+
end
|
13
|
+
class EncodableObject < BareEncodableObject
|
14
|
+
has_refcode :secret => SECRET, :salt => :record_id
|
15
|
+
end
|
16
|
+
|
10
17
|
def test_encoder_against_hash
|
11
18
|
test_hash = { :channel => 'email', :record_id => 49203, :action => 'forward' }
|
12
19
|
assert_equal test_hash, encode_and_decode(test_hash)
|
@@ -26,8 +33,40 @@ class RefcodeTest < Test::Unit::TestCase
|
|
26
33
|
assert_equal test_struct, encode_and_decode(test_struct)
|
27
34
|
end
|
28
35
|
|
36
|
+
def test_should_raise_argument_error_if_generate_refcode_called_without_val
|
37
|
+
assert_raise ArgumentError do
|
38
|
+
encodable_object.generate_refcode
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_should_raise_error_if_generate_refcode_called_before_has_refcode
|
43
|
+
assert_raise ArgumentError do
|
44
|
+
obj = BareEncodableObject.new :some => 'value'
|
45
|
+
obj.generate_refcode
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_should_raise_an_error_if_salt_is_empty
|
50
|
+
encodable_object.record_id = nil
|
51
|
+
assert_raise RuntimeError do
|
52
|
+
referral_code_content = { :channel => 'email', :action => 'forward', :user_id => 14325 }
|
53
|
+
code = encodable_object.generate_refcode referral_code_content
|
54
|
+
assert_equal referral_code_content, encodable_object.parse_refcode(code)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_encodable_against_hash
|
59
|
+
referral_code_content = { :channel => 'email', :action => 'forward', :user_id => 14325 }
|
60
|
+
code = encodable_object.generate_refcode referral_code_content
|
61
|
+
assert_equal referral_code_content, encodable_object.parse_refcode(code)
|
62
|
+
end
|
63
|
+
|
29
64
|
private
|
30
65
|
|
66
|
+
def encodable_object
|
67
|
+
@encodable_object ||= EncodableObject.new :name => 'Unimportant Value', :record_id => 381598
|
68
|
+
end
|
69
|
+
|
31
70
|
def encoder
|
32
71
|
@encoder ||= Refcode::Encoder.new do |e|
|
33
72
|
e.secret = SECRET
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: refcode
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jordan Sitkin
|
@@ -79,6 +79,8 @@ files:
|
|
79
79
|
- README.md
|
80
80
|
- Rakefile
|
81
81
|
- lib/refcode.rb
|
82
|
+
- lib/refcode/encodable.rb
|
83
|
+
- lib/refcode/encoder.rb
|
82
84
|
- lib/refcode/version.rb
|
83
85
|
- refcode.gemspec
|
84
86
|
- test/refcode_test.rb
|