masked_id 0.0.2 → 0.0.3

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.
@@ -1,4 +1,5 @@
1
- require_relative 'masked_id/baffler'
1
+ require 'masked_id/baffler'
2
+ require 'masked_id/model_extensions'
2
3
 
3
4
  def mask_id
4
5
  include MaskedId
@@ -10,13 +11,5 @@ module MaskedId
10
11
  def masked_id
11
12
  MaskedId::Baffler.baffle_id(self.id, self.class.name.to_i(36))
12
13
  end
13
-
14
- module ModelExtensions
15
-
16
- def find_by_masked_id(id)
17
- self.find(MaskedId::Baffler.debaffle_id(id, self.name.to_i(36)))
18
- end
19
-
20
- end
21
14
 
22
15
  end
@@ -0,0 +1,21 @@
1
+ #require_relative 'scatter_swap'
2
+ module MaskedId
3
+ class Baffler
4
+
5
+ def self.baffle_id(original_id, baffler_id)
6
+ original_id * baffler_id
7
+ #ScatterSwap.hash(original_id, baffler_id)
8
+ end
9
+
10
+ def self.debaffle_id(baffled_id, baffler_id)
11
+ baffled_id / baffler_id
12
+ #ScatterSwap.reverse_hash(baffled_id, baffler_id)
13
+ end
14
+
15
+ def debaffle_param(symbol)
16
+ params[symbol] = debaffle_id(symbol).to_s if params[symbol]
17
+ end
18
+
19
+ end
20
+
21
+ end
@@ -0,0 +1,140 @@
1
+ module MaskedId
2
+ class ScatterSwap
3
+ # This is the hashing function behind ObfuscateId.
4
+ # https://github.com/namick/obfuscate_id
5
+ #
6
+ # Designing a hash function is a bit of a black art and
7
+ # being that I don't have math background, I must resort
8
+ # to this simplistic swaping and scattering of array elements.
9
+ #
10
+ # After writing this and reading/learning some elemental hashing techniques,
11
+ # I realize this library is what is known as a Minimal perfect hash function:
12
+ # http://en.wikipedia.org/wiki/Perfect_hash_function#Minimal_perfect_hash_function
13
+ #
14
+ # I welcome all improvements :-)
15
+ #
16
+ # If you have some comments or suggestions, please contact me on github
17
+ # https://github.com/namick
18
+ #
19
+ # - nathan amick
20
+ #
21
+ #
22
+ # This library is built for integers that can be expressed with 10 digits:
23
+ # It zero pads smaller numbers... so the number one is expressed with:
24
+ # 0000000001
25
+ # The biggest number it can deal with is:
26
+ # 9999999999
27
+ #
28
+ # Since we are working with a limited sequential set of input integers, 10 billion,
29
+ # this algorithm will suffice for simple id obfuscation for many web apps.
30
+ # The largest value that Ruby on Rails default id, Mysql INT type, is just over 2 billion (2147483647)
31
+ # which is the same as 2 to the power of 31 minus 1, but considerably less than 10 billion.
32
+ #
33
+ # ScatterSwap is an integer hash function designed to have:
34
+ # - zero collisions ( http://en.wikipedia.org/wiki/Perfect_hash_function )
35
+ # - achieve avalanche ( http://en.wikipedia.org/wiki/Avalanche_effect )
36
+ # - reversable
37
+ #
38
+ # We do that by combining two distinct strategies.
39
+ #
40
+ # 1. Scattering - whereby we take the whole number, slice it up into 10 digits
41
+ # and rearange their places, yet retaining the same 10 digits. This allows
42
+ # us to preserve the sum of all the digits, regardless of order. This sum acts
43
+ # as a key to reverse this scattering effect.
44
+ #
45
+ # 2. Swapping - when dealing with many sequential numbers that we don't want
46
+ # to look similar, scattering wont do us much good because so many of the
47
+ # digits are the same; it deoesn't help to scatter 9 zeros around, so we need
48
+ # to swap out each of those zeros for something else.. something different
49
+ # for each place in the 10 digit array; for this, we need a map so that we
50
+ # can reverse it.
51
+
52
+ # Convience class method pointing to the instance method
53
+ def self.hash(plain_integer, spin = 0)
54
+ new(plain_integer, spin).hash
55
+ end
56
+
57
+ # Convience class method pointing to the instance method
58
+ def self.reverse_hash(hashed_integer, spin = 0)
59
+ new(hashed_integer, spin).reverse_hash
60
+ end
61
+
62
+ def initialize(original_integer, spin = 0)
63
+ @original_integer = original_integer
64
+ @spin = spin
65
+ zero_pad = original_integer.to_s.rjust(10, '0')
66
+ @working_array = zero_pad.split("").collect {|d| d.to_i}
67
+ end
68
+
69
+ attr_accessor :working_array
70
+
71
+ # obfuscates an integer up to 10 digits in length
72
+ def hash
73
+ swap
74
+ scatter
75
+ completed_string
76
+ end
77
+
78
+ # de-obfuscates an integer
79
+ def reverse_hash
80
+ unscatter
81
+ unswap
82
+ completed_string
83
+ end
84
+
85
+ def completed_string
86
+ @working_array.join
87
+ end
88
+
89
+ # We want a unique map for each place in the original number
90
+ def swapper_map(index)
91
+ array = (0..9).to_a
92
+ 10.times.collect.with_index do |i|
93
+ array.rotate!(index + i ^ spin).pop
94
+ end
95
+ end
96
+
97
+ # Using a unique map for each of the ten places,
98
+ # we swap out one number for another
99
+ def swap
100
+ @working_array = @working_array.collect.with_index do |digit, index|
101
+ swapper_map(index)[digit]
102
+ end
103
+ end
104
+
105
+ # Reverse swap
106
+ def unswap
107
+ @working_array = @working_array.collect.with_index do |digit, index|
108
+ swapper_map(index).rindex(digit)
109
+ end
110
+ end
111
+
112
+ # Rearrange the order of each digit in a reversable way by using the
113
+ # sum of the digits (which doesn't change regardless of order)
114
+ # as a key to record how they were scattered
115
+ def scatter
116
+ sum_of_digits = @working_array.inject(:+).to_i
117
+ @working_array = 10.times.collect do
118
+ @working_array.rotate!(spin ^ sum_of_digits).pop
119
+ end
120
+ end
121
+
122
+ # Reverse the scatter
123
+ def unscatter
124
+ scattered_array = @working_array
125
+ sum_of_digits = scattered_array.inject(:+).to_i
126
+ @working_array = []
127
+ @working_array.tap do |unscatter|
128
+ 10.times do
129
+ unscatter.push scattered_array.pop
130
+ unscatter.rotate! (sum_of_digits ^ spin) * -1
131
+ end
132
+ end
133
+ end
134
+
135
+ # Add some spice so that different apps can have differently mapped hashes
136
+ def spin
137
+ @spin || 0
138
+ end
139
+ end
140
+ end
metadata CHANGED
@@ -1,47 +1,65 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: masked_id
3
- version: !ruby/object:Gem::Version
4
- version: 0.0.2
5
- prerelease:
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 3
9
+ version: 0.0.3
6
10
  platform: ruby
7
- authors:
11
+ authors:
8
12
  - William Basmayor
9
13
  autorequire:
10
14
  bindir: bin
11
15
  cert_chain: []
12
- date: 2012-11-12 00:00:00.000000000 Z
16
+
17
+ date: 2013-02-05 00:00:00 +08:00
18
+ default_executable:
13
19
  dependencies: []
14
- description: Masks the original ID of a class with a unique obfuscated value per object
15
- type.
16
- email:
20
+
21
+ description: Masks the original ID of a class with a unique obfuscated value per object type.
22
+ email:
17
23
  - wbasmayor@gmail.com
18
24
  executables: []
25
+
19
26
  extensions: []
27
+
20
28
  extra_rdoc_files: []
21
- files:
29
+
30
+ files:
22
31
  - lib/masked_id.rb
32
+ - lib/masked_id/baffler.rb
33
+ - lib/masked_id/scatter_swap.rb
34
+ has_rdoc: true
23
35
  homepage:
24
36
  licenses: []
37
+
25
38
  post_install_message:
26
39
  rdoc_options: []
27
- require_paths:
40
+
41
+ require_paths:
28
42
  - lib
29
- required_ruby_version: !ruby/object:Gem::Requirement
30
- none: false
31
- requirements:
32
- - - ! '>='
33
- - !ruby/object:Gem::Version
34
- version: '0'
35
- required_rubygems_version: !ruby/object:Gem::Requirement
36
- none: false
37
- requirements:
38
- - - ! '>='
39
- - !ruby/object:Gem::Version
40
- version: '0'
43
+ required_ruby_version: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ segments:
48
+ - 0
49
+ version: "0"
50
+ required_rubygems_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ segments:
55
+ - 0
56
+ version: "0"
41
57
  requirements: []
58
+
42
59
  rubyforge_project:
43
- rubygems_version: 1.8.24
60
+ rubygems_version: 1.3.6
44
61
  signing_key:
45
62
  specification_version: 3
46
63
  summary: Unique ID masking by class.
47
64
  test_files: []
65
+