obfuscate_id 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: cd7d195e3e48bc52a6a503768fa97a6e3a1e2ecf
4
+ data.tar.gz: 06c755b17b92e191a58ee54c20e9660a04330ebd
5
+ SHA512:
6
+ metadata.gz: c414a598a21e07bdb67aa85e0a5d07808124cabf44ab6a7f91198a5a2579f74f6620a79c86edbf6e2b40bd705cc76342780b3b13dcd03d851524be4d4d2aedae
7
+ data.tar.gz: 44df8d341bea9f1a482e8488a50095402c0e50ccd2d0033e605557fce1a41bb8ca6cd04def070698a86ac432b59a46ae180bed2e33b8aa09032ee0f3ae0ab895
data/README.md CHANGED
@@ -81,3 +81,32 @@ ActiveRecord reverses this obfuscated id back to the plain id before building th
81
81
  * This is not security. obfuscate_id was created to lightly mask record id numbers for the casual user. If you need to really secure your database ids (hint, you probably don't), you need to use real encryption like AES.
82
82
  * To properly generate obfuscated urls, make sure you trigger the model's `to_param` method by passing in the whole object rather than just the id; do this: `post_path(@post)` not this: `post_path(@post.id)`.
83
83
 
84
+ ## Development
85
+
86
+ To run the tests, first clone the repo and run bundler:
87
+
88
+ git clone git@github.com:namick/obfuscate_id.git
89
+ cd obfuscate_id
90
+ bundle install
91
+
92
+ Change to the dummy rails app and load the test database
93
+
94
+ cd spec/dummy
95
+ bundle exec rake db:test:load
96
+ cd -
97
+
98
+ Run the tests
99
+
100
+ bundle exec rspec spec
101
+
102
+ Or have Guard run them continuously
103
+
104
+ bundle exec guard
105
+
106
+ ## Contributing
107
+
108
+ 1. Fork it
109
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
110
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
111
+ 4. Push to the branch (`git push origin my-new-feature`)
112
+ 5. Create new Pull Request
data/lib/obfuscate_id.rb CHANGED
@@ -1,7 +1,8 @@
1
1
  module ObfuscateId
2
2
 
3
3
  def obfuscate_id(options = {})
4
- require 'obfuscate_id/scatter_swap'
4
+ require 'scatter_swap'
5
+
5
6
  extend ClassMethods
6
7
  include InstanceMethods
7
8
  cattr_accessor :obfuscate_id_spin
@@ -19,16 +20,27 @@ module ObfuscateId
19
20
 
20
21
  module ClassMethods
21
22
  def find(*args)
22
- if has_obfuscated_id?
23
- args[0] = ObfuscateId.show(args[0], self.obfuscate_id_spin)
23
+ scope = args.slice!(0)
24
+ options = args.slice!(0) || {}
25
+ if has_obfuscated_id? && !options[:no_obfuscated_id]
26
+ if scope.is_a?(Array)
27
+ scope.map! {|a| deobfuscate_id(a).to_i}
28
+ else
29
+ scope = deobfuscate_id(scope)
30
+ end
24
31
  end
25
- super(*args)
32
+ options.delete(:no_obfuscated_id)
33
+ super(scope, options)
26
34
  end
27
35
 
28
36
  def has_obfuscated_id?
29
37
  true
30
38
  end
31
39
 
40
+ def deobfuscate_id(obfuscated_id)
41
+ ObfuscateId.show(obfuscated_id, self.obfuscate_id_spin)
42
+ end
43
+
32
44
  # Generate a default spin from the Model name
33
45
  # This makes it easy to drop obfuscate_id onto any model
34
46
  # and produce different obfuscated ids for different models
@@ -47,14 +59,15 @@ module ObfuscateId
47
59
  ObfuscateId.hide(self.id, self.class.obfuscate_id_spin)
48
60
  end
49
61
 
50
- # Temporarily set the id to the parameterized version,
51
- # as ActiveRecord::Persistence#reload uses self.id.
52
- def reload(options=nil)
53
- actual_id = self.id
54
- self.id = to_param
55
- super(options).tap do
56
- self.id = actual_id
57
- end
62
+ # As ActiveRecord::Persistence#reload uses self.id
63
+ # reload without deobfuscating
64
+ def reload(options = nil)
65
+ options = (options || {}).merge(:no_obfuscated_id => true)
66
+ super(options)
67
+ end
68
+
69
+ def deobfuscate_id(obfuscated_id)
70
+ self.class.deobfuscate_id(obfuscated_id)
58
71
  end
59
72
  end
60
73
  end
@@ -1,3 +1,3 @@
1
1
  module ObfuscateId
2
- VERSION = "0.0.3"
2
+ VERSION = "0.0.4"
3
3
  end
metadata CHANGED
@@ -1,126 +1,125 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: obfuscate_id
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
5
- prerelease:
4
+ version: 0.0.4
6
5
  platform: ruby
7
6
  authors:
8
7
  - Nathan Amick
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2013-03-04 00:00:00.000000000 Z
11
+ date: 2013-10-29 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
- name: rails
14
+ name: scatter_swap
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
17
  - - ~>
20
18
  - !ruby/object:Gem::Version
21
- version: 3.2.1
19
+ version: 0.0.3
22
20
  type: :runtime
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
24
  - - ~>
28
25
  - !ruby/object:Gem::Version
29
- version: 3.2.1
26
+ version: 0.0.3
27
+ - !ruby/object:Gem::Dependency
28
+ name: rails
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '='
32
+ - !ruby/object:Gem::Version
33
+ version: 3.2.15
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '='
39
+ - !ruby/object:Gem::Version
40
+ version: 3.2.15
30
41
  - !ruby/object:Gem::Dependency
31
42
  name: sqlite3
32
43
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
44
  requirements:
35
- - - ! '>='
45
+ - - '>='
36
46
  - !ruby/object:Gem::Version
37
47
  version: '0'
38
48
  type: :development
39
49
  prerelease: false
40
50
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
51
  requirements:
43
- - - ! '>='
52
+ - - '>='
44
53
  - !ruby/object:Gem::Version
45
54
  version: '0'
46
55
  - !ruby/object:Gem::Dependency
47
56
  name: rspec-rails
48
57
  requirement: !ruby/object:Gem::Requirement
49
- none: false
50
58
  requirements:
51
- - - ! '>='
59
+ - - '>='
52
60
  - !ruby/object:Gem::Version
53
61
  version: '0'
54
62
  type: :development
55
63
  prerelease: false
56
64
  version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
65
  requirements:
59
- - - ! '>='
66
+ - - '>='
60
67
  - !ruby/object:Gem::Version
61
68
  version: '0'
62
69
  - !ruby/object:Gem::Dependency
63
70
  name: capybara
64
71
  requirement: !ruby/object:Gem::Requirement
65
- none: false
66
72
  requirements:
67
- - - ! '>='
73
+ - - '>='
68
74
  - !ruby/object:Gem::Version
69
75
  version: '0'
70
76
  type: :development
71
77
  prerelease: false
72
78
  version_requirements: !ruby/object:Gem::Requirement
73
- none: false
74
79
  requirements:
75
- - - ! '>='
80
+ - - '>='
76
81
  - !ruby/object:Gem::Version
77
82
  version: '0'
78
83
  - !ruby/object:Gem::Dependency
79
84
  name: guard-rspec
80
85
  requirement: !ruby/object:Gem::Requirement
81
- none: false
82
86
  requirements:
83
- - - ! '>='
87
+ - - '>='
84
88
  - !ruby/object:Gem::Version
85
89
  version: '0'
86
90
  type: :development
87
91
  prerelease: false
88
92
  version_requirements: !ruby/object:Gem::Requirement
89
- none: false
90
93
  requirements:
91
- - - ! '>='
94
+ - - '>='
92
95
  - !ruby/object:Gem::Version
93
96
  version: '0'
94
97
  - !ruby/object:Gem::Dependency
95
98
  name: guard-spork
96
99
  requirement: !ruby/object:Gem::Requirement
97
- none: false
98
100
  requirements:
99
- - - ! '>='
101
+ - - '>='
100
102
  - !ruby/object:Gem::Version
101
103
  version: '0'
102
104
  type: :development
103
105
  prerelease: false
104
106
  version_requirements: !ruby/object:Gem::Requirement
105
- none: false
106
107
  requirements:
107
- - - ! '>='
108
+ - - '>='
108
109
  - !ruby/object:Gem::Version
109
110
  version: '0'
110
111
  - !ruby/object:Gem::Dependency
111
112
  name: rb-inotify
112
113
  requirement: !ruby/object:Gem::Requirement
113
- none: false
114
114
  requirements:
115
- - - ! '>='
115
+ - - '>='
116
116
  - !ruby/object:Gem::Version
117
117
  version: '0'
118
118
  type: :development
119
119
  prerelease: false
120
120
  version_requirements: !ruby/object:Gem::Requirement
121
- none: false
122
121
  requirements:
123
- - - ! '>='
122
+ - - '>='
124
123
  - !ruby/object:Gem::Version
125
124
  version: '0'
126
125
  description: Make your ActiveRecord IDs non-obvious
@@ -130,36 +129,34 @@ executables: []
130
129
  extensions: []
131
130
  extra_rdoc_files: []
132
131
  files:
133
- - lib/tasks/obfuscate_id_tasks.rake
134
- - lib/obfuscate_id.rb
135
132
  - lib/obfuscate_id/version.rb
136
- - lib/obfuscate_id/scatter_swap.rb
137
- - lib/obfuscate_id/run_scatter_swap.rb
133
+ - lib/obfuscate_id.rb
134
+ - lib/tasks/obfuscate_id_tasks.rake
138
135
  - MIT-LICENSE
139
136
  - Rakefile
140
137
  - README.md
141
- homepage:
138
+ homepage: https://github.com/namick/obfuscate_id
142
139
  licenses: []
140
+ metadata: {}
143
141
  post_install_message:
144
142
  rdoc_options: []
145
143
  require_paths:
146
144
  - lib
147
145
  required_ruby_version: !ruby/object:Gem::Requirement
148
- none: false
149
146
  requirements:
150
- - - ! '>='
147
+ - - '>='
151
148
  - !ruby/object:Gem::Version
152
149
  version: '0'
153
150
  required_rubygems_version: !ruby/object:Gem::Requirement
154
- none: false
155
151
  requirements:
156
- - - ! '>='
152
+ - - '>='
157
153
  - !ruby/object:Gem::Version
158
154
  version: '0'
159
155
  requirements: []
160
156
  rubyforge_project:
161
- rubygems_version: 1.8.23
157
+ rubygems_version: 2.0.6
162
158
  signing_key:
163
- specification_version: 3
159
+ specification_version: 4
164
160
  summary: Mask ActiveRecord IDs
165
161
  test_files: []
162
+ has_rdoc:
@@ -1,93 +0,0 @@
1
- require './scatter_swap.rb'
2
-
3
- # This file isn't really part of the library, its pretty much spike code..
4
- #
5
- # While developing this, I used this file to visualize what was going on with the numbers
6
- #
7
- # You can uncomment various methods at the bottom and then run it like this:
8
- #
9
- # watch -n1 ruby run_scatter_swap.rb
10
- #
11
- # tweak the code a bit and see instant visual changes in the generated numbers
12
-
13
- def visualize_scatter_and_unscatter
14
- # change this number to experiment with different values
15
- rotations = 99
16
-
17
- original = ScatterSwap.arrayify(123456789)
18
- scattered = []
19
- unscattered = []
20
- puts original.join
21
- puts "rotate!(#{rotations})"
22
- 10.times do
23
- puts original.rotate!(rotations).join + "->" + scattered.push(original.pop).join
24
- end
25
- puts "scattered"
26
- puts scattered.join
27
- 10.times do
28
- puts unscattered.push(scattered.pop).join + "->" + unscattered.rotate!(rotations * -1).join
29
- end
30
-
31
- puts unscattered.join
32
- end
33
-
34
-
35
- def visualize_swapper_map
36
- puts "swapper map"
37
- 10.times do |index|
38
- key = 1
39
- puts ScatterSwap.swapper_map(index).join.to_s
40
- end
41
- end
42
-
43
- def visualize_hash
44
- puts "hash"
45
- 40.times do |integer|
46
- output = "|"
47
- 3.times do |index|
48
- output += " #{(integer + (123456789 * index)).to_s.rjust(5, ' ')}"
49
- output += " => #{hashed = ScatterSwap.hash(integer + (123456789 * index) ) }"
50
- output += " => #{ScatterSwap.reverse_hash(hashed) } |"
51
- end
52
- puts output
53
- end
54
- end
55
-
56
-
57
- def visualize_scatter
58
- puts "original scattered unscattered"
59
- 20.times do |integer|
60
- output = ""
61
- 2.times do |index|
62
- original = ScatterSwap.arrayify(integer + (123456789 * index))
63
- scattered = ScatterSwap.scatter(original.clone)
64
- unscattered = ScatterSwap.unscatter(scattered.clone)
65
- output += "#{original.join}"
66
- output += " => #{scattered.join}"
67
- output += " => #{unscattered.join} | "
68
- end
69
- puts output
70
- end
71
- end
72
-
73
-
74
- # find hash for lots of spins
75
- def visualize_spin
76
- 2000.times do |original|
77
- hashed_values = []
78
- 9000000000.times do |spin|
79
- hashed = ScatterSwap.hash(original, spin)
80
- if hashed_values.include? hashed
81
- puts "collision: #{original} - #{spin} - #{hashed}"
82
- break
83
- end
84
- hashed_values.push hashed
85
- end
86
- end
87
- end
88
-
89
- #visualize_spin
90
- #visualize_hash
91
- #visualize_scatter_and_unscatter
92
- #visualize_scatter
93
- #visualize_swapper_map
@@ -1,138 +0,0 @@
1
- class ScatterSwap
2
- # This is the hashing function behind ObfuscateId.
3
- # https://github.com/namick/obfuscate_id
4
- #
5
- # Designing a hash function is a bit of a black art and
6
- # being that I don't have math background, I must resort
7
- # to this simplistic swaping and scattering of array elements.
8
- #
9
- # After writing this and reading/learning some elemental hashing techniques,
10
- # I realize this library is what is known as a Minimal perfect hash function:
11
- # http://en.wikipedia.org/wiki/Perfect_hash_function#Minimal_perfect_hash_function
12
- #
13
- # I welcome all improvements :-)
14
- #
15
- # If you have some comments or suggestions, please contact me on github
16
- # https://github.com/namick
17
- #
18
- # - nathan amick
19
- #
20
- #
21
- # This library is built for integers that can be expressed with 10 digits:
22
- # It zero pads smaller numbers... so the number one is expressed with:
23
- # 0000000001
24
- # The biggest number it can deal with is:
25
- # 9999999999
26
- #
27
- # Since we are working with a limited sequential set of input integers, 10 billion,
28
- # this algorithm will suffice for simple id obfuscation for many web apps.
29
- # The largest value that Ruby on Rails default id, Mysql INT type, is just over 2 billion (2147483647)
30
- # which is the same as 2 to the power of 31 minus 1, but considerably less than 10 billion.
31
- #
32
- # ScatterSwap is an integer hash function designed to have:
33
- # - zero collisions ( http://en.wikipedia.org/wiki/Perfect_hash_function )
34
- # - achieve avalanche ( http://en.wikipedia.org/wiki/Avalanche_effect )
35
- # - reversable
36
- #
37
- # We do that by combining two distinct strategies.
38
- #
39
- # 1. Scattering - whereby we take the whole number, slice it up into 10 digits
40
- # and rearange their places, yet retaining the same 10 digits. This allows
41
- # us to preserve the sum of all the digits, regardless of order. This sum acts
42
- # as a key to reverse this scattering effect.
43
- #
44
- # 2. Swapping - when dealing with many sequential numbers that we don't want
45
- # to look similar, scattering wont do us much good because so many of the
46
- # digits are the same; it deoesn't help to scatter 9 zeros around, so we need
47
- # to swap out each of those zeros for something else.. something different
48
- # for each place in the 10 digit array; for this, we need a map so that we
49
- # can reverse it.
50
-
51
- # Convience class method pointing to the instance method
52
- def self.hash(plain_integer, spin = 0)
53
- new(plain_integer, spin).hash
54
- end
55
-
56
- # Convience class method pointing to the instance method
57
- def self.reverse_hash(hashed_integer, spin = 0)
58
- new(hashed_integer, spin).reverse_hash
59
- end
60
-
61
- def initialize(original_integer, spin = 0)
62
- @original_integer = original_integer
63
- @spin = spin
64
- zero_pad = original_integer.to_s.rjust(10, '0')
65
- @working_array = zero_pad.split("").collect {|d| d.to_i}
66
- end
67
-
68
- attr_accessor :working_array
69
-
70
- # obfuscates an integer up to 10 digits in length
71
- def hash
72
- swap
73
- scatter
74
- completed_string
75
- end
76
-
77
- # de-obfuscates an integer
78
- def reverse_hash
79
- unscatter
80
- unswap
81
- completed_string
82
- end
83
-
84
- def completed_string
85
- @working_array.join
86
- end
87
-
88
- # We want a unique map for each place in the original number
89
- def swapper_map(index)
90
- array = (0..9).to_a
91
- 10.times.collect.with_index do |i|
92
- array.rotate!(index + i ^ spin).pop
93
- end
94
- end
95
-
96
- # Using a unique map for each of the ten places,
97
- # we swap out one number for another
98
- def swap
99
- @working_array = @working_array.collect.with_index do |digit, index|
100
- swapper_map(index)[digit]
101
- end
102
- end
103
-
104
- # Reverse swap
105
- def unswap
106
- @working_array = @working_array.collect.with_index do |digit, index|
107
- swapper_map(index).rindex(digit)
108
- end
109
- end
110
-
111
- # Rearrange the order of each digit in a reversable way by using the
112
- # sum of the digits (which doesn't change regardless of order)
113
- # as a key to record how they were scattered
114
- def scatter
115
- sum_of_digits = @working_array.inject(:+).to_i
116
- @working_array = 10.times.collect do
117
- @working_array.rotate!(spin ^ sum_of_digits).pop
118
- end
119
- end
120
-
121
- # Reverse the scatter
122
- def unscatter
123
- scattered_array = @working_array
124
- sum_of_digits = scattered_array.inject(:+).to_i
125
- @working_array = []
126
- @working_array.tap do |unscatter|
127
- 10.times do
128
- unscatter.push scattered_array.pop
129
- unscatter.rotate! (sum_of_digits ^ spin) * -1
130
- end
131
- end
132
- end
133
-
134
- # Add some spice so that different apps can have differently mapped hashes
135
- def spin
136
- @spin || 0
137
- end
138
- end