active_hashcash 0.1.1 → 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2a52910cc5af45ca4b31ce6af112ce45a4ec401e584d80076aa50247f9dfc154
4
- data.tar.gz: 03ca632ff343a29d9841cb950684c693b4d7684b1bd42251731fea8f8f1c36c8
3
+ metadata.gz: 81d808ca54efb58e16c61524bde0aee355f5a7b003ef4cb6cda2e9b672c7a284
4
+ data.tar.gz: 6ec6f99dd02e353bb4e86bb9db8e65ecf35dc46e13051e401b62608700b7fde5
5
5
  SHA512:
6
- metadata.gz: b2758deaf12537653c08a27a988aa18e13449975caaf73e854a39ad5011eca63430ac1eb019d9dc5bb66d415a0d8975a0908c833056d0422fdf8a1b192d3316e
7
- data.tar.gz: 579afe8ab7e448ffd550882fad8b4c8e724be3ec5ab043aac57c49463097a738cf66fd9700193d4f418a01f6ffb03d29510d5593b4800c659548abce968f81fc
6
+ metadata.gz: 17f8b181f15b009b850fea2e1ead3a5cb904218d8877b31d9b6a2e5f2578972a6a1a2837cdbda46013987911a88198d543dd7f3a79f68b455a969a005a3fbfc8
7
+ data.tar.gz: 2b02561bf0516b2accb42ac64fe8d93b7bf956bb9474fcadec93db08f4be551d9eea430884d133d08b92156a9d10440ac8f48067207c95d3b72e4268752748dd
data/CHANGELOG.md CHANGED
@@ -1,4 +1,6 @@
1
- ## [Unreleased]
1
+ ## 0.2.0 - 2022-08-02
2
+
3
+ - Add ActiveHashcash::Store#clean to removed expired stamps
2
4
 
3
5
  ## [0.1.1] - 2022-07-08
4
6
 
data/README.md CHANGED
@@ -66,6 +66,8 @@ You must have Redis in order to prevent double spent stamps. Otherwise it will b
66
66
  It automatically tries to connect with the environement variables `ACTIVE_HASHCASH_REDIS_URL` or `REDIS_URL`.
67
67
  You can also manually set the URL with `ActiveHashcash.redis_url = redis://user:password@localhost:6379`.
68
68
 
69
+ You should call `ActiveHashcash::Store#clean` once a day, to remove expired stamps.
70
+
69
71
  ## Complexity
70
72
 
71
73
  Complexity is the most important parameter. By default its value is 20 and requires most of the time 5 to 20 seconds to be solved on a decent laptop.
@@ -0,0 +1,19 @@
1
+ module ActiveHashcash
2
+ class Engine < ::Rails::Engine
3
+ config.assets.paths << File.expand_path("../..", __FILE__)
4
+
5
+ config.after_initialize { load_translations }
6
+
7
+ def load_translations
8
+ if !I18n.backend.exists?(I18n.locale, "active_hashcash")
9
+ I18n.backend.store_translations(:de, {active_hashcash: {waiting_label: "Warten auf die Überprüfung ..."}})
10
+ I18n.backend.store_translations(:en, {active_hashcash: {waiting_label: "Waiting for verification ..."}})
11
+ I18n.backend.store_translations(:es, {active_hashcash: {waiting_label: "A la espera de la verificación ..."}})
12
+ I18n.backend.store_translations(:fr, {active_hashcash: {waiting_label: "En attente de vérification ..."}})
13
+ I18n.backend.store_translations(:it, {active_hashcash: {waiting_label: "In attesa di verifica ..."}})
14
+ I18n.backend.store_translations(:jp, {active_hashcash: {waiting_label: "検証待ち ..."}})
15
+ I18n.backend.store_translations(:pt, {active_hashcash: {waiting_label: "À espera de verificação ..."}})
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,52 @@
1
+ module ActiveHashcash
2
+ class Stamp
3
+ attr_reader :version, :bits, :date, :resource, :extension, :rand, :counter
4
+
5
+ def self.parse(string)
6
+ args = string.split(":")
7
+ new(args[0], args[1], args[2], args[3], args[4], args[5], args[6])
8
+ end
9
+
10
+ def self.mint(resource, options = {})
11
+ new(
12
+ options[:version] || 1,
13
+ options[:bits] || ActiveHashcash.bits,
14
+ options[:date] || Date.today.strftime("%y%m%d"),
15
+ resource,
16
+ options[:ext],
17
+ options[:rand] || SecureRandom.alphanumeric(16),
18
+ options[:counter] || 0).work
19
+ end
20
+
21
+ def initialize(version, bits, date, resource, extension, rand, counter)
22
+ @version = version
23
+ @bits = bits.to_i
24
+ @date = date.respond_to?(:strftime) ? date.strftime("%y%m%d") : date
25
+ @resource = resource
26
+ @extension = extension
27
+ @rand = rand
28
+ @counter = counter
29
+ end
30
+
31
+ def valid?
32
+ Digest::SHA1.hexdigest(to_s).hex >> (160-bits) == 0
33
+ end
34
+
35
+ def verify(resource, bits, date)
36
+ self.resource == resource && self.bits >= bits && parse_date >= date && valid?
37
+ end
38
+
39
+ def to_s
40
+ [version, bits, date, resource, extension, rand, counter].join(":")
41
+ end
42
+
43
+ def parse_date
44
+ Date.strptime(date, "%y%m%d")
45
+ end
46
+
47
+ def work
48
+ @counter += 1 until valid?
49
+ self
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,25 @@
1
+ module ActiveHashcash
2
+ class Store
3
+ attr_reader :redis
4
+
5
+ def initialize(redis = Redis.new(url: ActiveHashcash.redis_url || ENV["ACTIVE_HASHCASH_REDIS_URL"] || ENV["REDIS_URL"]))
6
+ @redis = redis
7
+ end
8
+
9
+ def add?(stamp)
10
+ redis.sadd("active_hashcash_stamps_#{stamp.date}", stamp) ? self : nil
11
+ end
12
+
13
+ def clear
14
+ redis.del(redis.keys("active_hashcash_stamps*"))
15
+ end
16
+
17
+ def clean
18
+ today = Date.today.strftime("%y%m%d")
19
+ yesterday = (Date.today - 1).strftime("%y%m%d")
20
+ keep = ["active_hashcash_stamps_#{today}", "active_hashcash_stamps_#{yesterday}"]
21
+ keys = redis.keys("active_hashcash_stamps*")
22
+ redis.del(keys - keep)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,3 @@
1
+ module ActiveHashcash
2
+ VERSION = "0.2.0"
3
+ end
@@ -9,14 +9,22 @@ module ActiveHashcash
9
9
 
10
10
  mattr_accessor :bits, instance_accessor: false, default: 20
11
11
  mattr_accessor :resource, instance_accessor: false
12
- mattr_accessor :redis_url, instance_accessor: false
12
+ mattr_accessor :redis_url, instance_accessor: false, default: ENV["ACTIVE_HASHCASH_REDIS_URL"] || ENV["REDIS_URL"]
13
+
14
+ def self.store
15
+ @store ||= Store.new(Redis.new(url: ActiveHashcash.redis_url))
16
+ end
17
+
18
+ def self.store=(store)
19
+ @store = store
20
+ end
13
21
 
14
22
  # TODO: protect_from_brute_force bits: 20, exception: ActionController::InvalidAuthenticityToken, with: :handle_failed_hashcash
15
23
 
16
24
  # Call me via a before_action when the form is submitted : `before_action :chech_hashcash, only: :create`
17
25
  def check_hashcash
18
- if hashcash_stamp_is_valid? && !hashcash_stamp_spent?
19
- hashcash_redis.sadd("active_hashcash_stamps".freeze, hashcash_param)
26
+ stamp = hashcash_param && Stamp.parse(hashcash_param)
27
+ if stamp && stamp.verify(hashcash_resource, hashcash_bits, Date.yesterday) && ActiveHashcash.store.add?(stamp)
20
28
  hashcash_after_success
21
29
  else
22
30
  hashcash_after_failure
@@ -63,88 +71,8 @@ module ActiveHashcash
63
71
  options = {resource: hashcash_resource, bits: hashcash_bits, waiting_message: hashcash_waiting_message}
64
72
  hidden_field_tag(name, "", "data-hashcash" => options.to_json)
65
73
  end
66
-
67
- def hashcash_redis
68
- @hashcash_redis = Redis.new(url: hashcash_redis_url)
69
- end
70
-
71
- def hashcash_redis_url
72
- ActiveHashcash.redis_url || ENV["ACTIVE_HASHCASH_REDIS_URL"] || ENV["REDIS_URL"]
73
- end
74
-
75
- def hashcash_stamp_is_valid?
76
- stamp = hashcash_param && Stamp.parse(hashcash_param)
77
- stamp && stamp.valid? && stamp.bits >= hashcash_bits && stamp.parse_date >= Date.yesterday
78
- end
79
-
80
- def hashcash_stamp_spent?
81
- hashcash_redis.sismember("active_hashcash_stamps".freeze, hashcash_param)
82
- end
83
-
84
-
85
-
86
- class Engine < ::Rails::Engine
87
- config.assets.paths << File.expand_path("..", __FILE__)
88
-
89
- config.after_initialize { load_translations }
90
-
91
- def load_translations
92
- if !I18n.backend.exists?(I18n.locale, "active_hashcash")
93
- I18n.backend.store_translations(:de, {active_hashcash: {waiting_label: "Warten auf die Überprüfung ..."}})
94
- I18n.backend.store_translations(:en, {active_hashcash: {waiting_label: "Waiting for verification ..."}})
95
- I18n.backend.store_translations(:es, {active_hashcash: {waiting_label: "A la espera de la verificación ..."}})
96
- I18n.backend.store_translations(:fr, {active_hashcash: {waiting_label: "En attente de vérification ..."}})
97
- I18n.backend.store_translations(:it, {active_hashcash: {waiting_label: "In attesa di verifica ..."}})
98
- I18n.backend.store_translations(:jp, {active_hashcash: {waiting_label: "検証待ち ..."}})
99
- I18n.backend.store_translations(:pt, {active_hashcash: {waiting_label: "À espera de verificação ..."}})
100
- end
101
- end
102
- end
103
-
104
- class Stamp
105
- attr_reader :version, :bits, :date, :resource, :extension, :rand, :counter
106
-
107
- def self.parse(string)
108
- args = string.split(":")
109
- new(args[0], args[1], args[2], args[3], args[4], args[5], args[6])
110
- end
111
-
112
- def self.mint(resource, options = {})
113
- new(
114
- options[:version] || 1,
115
- options[:bits] || ActiveHashcash.bits,
116
- options[:date] || Date.today.strftime("%y%m%d"),
117
- resource,
118
- options[:ext],
119
- options[:rand] || SecureRandom.alphanumeric(16),
120
- options[:counter] || 0).work
121
- end
122
-
123
- def initialize(version, bits, date, resource, extension, rand, counter)
124
- @version = version
125
- @bits = bits.to_i
126
- @date = date.respond_to?(:strftime) ? date.strftime("%y%m%d") : date
127
- @resource = resource
128
- @extension = extension
129
- @rand = rand
130
- @counter = counter
131
- end
132
-
133
- def valid?
134
- Digest::SHA1.hexdigest(to_s).hex >> (160-bits) == 0
135
- end
136
-
137
- def to_s
138
- [version, bits, date, resource, extension, rand, counter].join(":")
139
- end
140
-
141
- def parse_date
142
- Date.strptime(date, "%y%m%d")
143
- end
144
-
145
- def work
146
- @counter += 1 until valid?
147
- self
148
- end
149
- end
150
74
  end
75
+
76
+ require "active_hashcash/stamp"
77
+ require "active_hashcash/store"
78
+ require "active_hashcash/engine"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_hashcash
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexis Bernard
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-07-08 00:00:00.000000000 Z
11
+ date: 2022-08-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis
@@ -50,6 +50,10 @@ files:
50
50
  - LICENSE.txt
51
51
  - README.md
52
52
  - lib/active_hashcash.rb
53
+ - lib/active_hashcash/engine.rb
54
+ - lib/active_hashcash/stamp.rb
55
+ - lib/active_hashcash/store.rb
56
+ - lib/active_hashcash/version.rb
53
57
  - lib/hashcash.js
54
58
  homepage: https://github.com/BaseSecrete/active_hashcash
55
59
  licenses: