saro-dat 4.0.0 → 4.3.1

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: a1981b6c7f0f207f6388f95f4cafaee56a44c49209b0961567f8f37dc3cb2187
4
- data.tar.gz: 0e96927af41f3fb6c387a5ffceec5065f8ff6d2e723b8ed6dce41068e7c523fd
3
+ metadata.gz: 56415a92654a73befef882b2ca36674095155444492b8e8c8db99ebb4c3cd7cb
4
+ data.tar.gz: 670ad624aa1372b2d33f1bae79b877b462970ec20e4a40d1419dd12b3fceb27a
5
5
  SHA512:
6
- metadata.gz: c4c8b7d3730883f2056841f0018ae4783223ffe417c35861a62ae56d6f0010a5b3d9687adbd6aeb7a87f358d9182af396df007313a493e0073681b4e4b474d72
7
- data.tar.gz: 7af21459544e31f18c9edcae209fcb3181cdc40d6609e80b65ff8c4b002502c1687e7631e4ae2cd224d01a017447ee421a5ee754602e11d95f4616a792c06169
6
+ metadata.gz: '087cad97ce30b7d51e1e0ebed1c30749fd33c59e5cb7ccad1d197e21502f0d5e341fb713f937cd8315b0a56e5d94e1170a061d1967ce8778ec2e926ac7535b19'
7
+ data.tar.gz: a1c460e69d89564b6628d4b264d9f523133c350f0769caae6dff8b401822be8de59223889d9baf8cf7ed535acf15f1698852d60cc2e40da04a26dec4b587d7a4
data/.idea/saro-dat.iml CHANGED
@@ -14,7 +14,8 @@
14
14
  <orderEntry type="library" scope="PROVIDED" name="base64 (v0.3.0, rbenv: 4.0.5) [gem]" level="application" />
15
15
  <orderEntry type="library" scope="PROVIDED" name="benchmark (v0.5.0, rbenv: 4.0.5) [gem]" level="application" />
16
16
  <orderEntry type="library" scope="PROVIDED" name="bundler (v4.0.12, rbenv: 4.0.5) [gem]" level="application" />
17
- <orderEntry type="library" scope="PROVIDED" name="concurrent-ruby (v1.3.6, rbenv: 4.0.5) [gem]" level="application" />
17
+ <orderEntry type="library" scope="PROVIDED" name="concurrent-ruby (v1.3.7, rbenv: 4.0.5) [gem]" level="application" />
18
+ <orderEntry type="library" scope="PROVIDED" name="logger (v1.7.0, rbenv: 4.0.5) [gem]" level="application" />
18
19
  <orderEntry type="library" scope="PROVIDED" name="minitest (v5.27.0, rbenv: 4.0.5) [gem]" level="application" />
19
20
  <orderEntry type="library" scope="PROVIDED" name="openssl (v4.0.2, rbenv: 4.0.5) [gem]" level="application" />
20
21
  <orderEntry type="library" scope="PROVIDED" name="parallel (v2.1.0, rbenv: 4.0.5) [gem]" level="application" />
data/PUBLISH.md CHANGED
@@ -9,7 +9,7 @@ bundle install
9
9
  ```
10
10
  gem build saro-dat.gemspec
11
11
  gem signin
12
- gem push saro-dat-4.0.0.gem
12
+ gem push saro-dat-4.3.0.gem
13
13
  ```
14
14
 
15
15
  ## install
data/README.md CHANGED
@@ -4,9 +4,9 @@
4
4
 
5
5
  ### [DAT Run Online](https://dat.saro.me)
6
6
 
7
- ### [What is DAT](https://dat.saro.me/--/intro)
7
+ ### [What is DAT](https://dat.saro.me/intro)
8
8
 
9
- ### [Example](https://dat.saro.me/--/libs/gems-saro-dat)
9
+ ### [Example](https://dat.saro.me/libs/gems-saro-dat)
10
10
 
11
11
  ## Support algorithm
12
12
  ### Signature
@@ -7,33 +7,33 @@ require_relative 'util'
7
7
  module Saro
8
8
  module Dat
9
9
  class DatCertificate
10
- attr_reader :cid, :signature_key, :crypto_key, :dat_issue_begin, :dat_issue_end, :dat_ttl
10
+ attr_reader :cid, :signature_key, :crypto_key, :dat_issuance_start_seconds, :dat_issuance_end_seconds, :dat_ttl_seconds
11
11
 
12
- def initialize(cid, issued_at, issuance_duration, dat_ttl, signature_key, crypto_key)
12
+ def initialize(cid, dat_issuance_start_seconds, dat_issuance_duration_seconds, dat_ttl_seconds, signature_key, crypto_key)
13
13
  @cid = cid
14
- @dat_issue_begin = issued_at
15
- @dat_issue_end = issued_at + issuance_duration
16
- @dat_ttl = dat_ttl
14
+ @dat_issuance_start_seconds = dat_issuance_start_seconds
15
+ @dat_issuance_end_seconds = dat_issuance_start_seconds + dat_issuance_duration_seconds
16
+ @dat_ttl_seconds = dat_ttl_seconds
17
17
  @signature_key = signature_key
18
18
  @crypto_key = crypto_key
19
19
  end
20
20
 
21
21
  def exports(verify_only = false)
22
22
  cid_hex = @cid.to_s(16)
23
- issued_at = @dat_issue_begin.to_s
24
- issuance_duration = (@dat_issue_end - @dat_issue_begin).to_s
25
- ttl = @dat_ttl.to_s
26
- sig_alg = @signature_key.algorithm
27
- cry_alg = @crypto_key.algorithm
28
- sig_key = @signature_key.exports(verify_only)
29
- cry_key = @crypto_key.exports
23
+ dat_issuance_start_seconds = @dat_issuance_start_seconds.to_s
24
+ dat_issuance_duration_seconds = (@dat_issuance_end_seconds - @dat_issuance_start_seconds).to_s
25
+ dat_ttl_seconds = @dat_ttl_seconds.to_s
26
+ signature_algorithm = @signature_key.algorithm
27
+ crypto_algorithm = @crypto_key.algorithm
28
+ signature_key = @signature_key.exports(verify_only)
29
+ crypto_key = @crypto_key.exports
30
30
 
31
- "#{cid_hex}.#{issued_at}.#{issuance_duration}.#{ttl}.#{sig_alg}.#{cry_alg}.#{sig_key}.#{cry_key}"
31
+ "#{cid_hex}.#{dat_issuance_start_seconds}.#{dat_issuance_duration_seconds}.#{dat_ttl_seconds}.#{signature_algorithm}.#{crypto_algorithm}.#{signature_key}.#{crypto_key}"
32
32
  end
33
33
 
34
- def self.generate(cid, issued_at, issuance_duration, dat_ttl, signature_algorithm, crypto_algorithm)
34
+ def self.generate(cid, dat_issuance_start_seconds, dat_issuance_duration_seconds, dat_ttl_seconds, signature_algorithm, crypto_algorithm)
35
35
  new(
36
- cid, issued_at, issuance_duration, dat_ttl,
36
+ cid, dat_issuance_start_seconds, dat_issuance_duration_seconds, dat_ttl_seconds,
37
37
  Saro::Dat::DatSignature.generate(signature_algorithm),
38
38
  Saro::Dat::DatCrypto.generate(crypto_algorithm)
39
39
  )
@@ -44,24 +44,24 @@ module Saro
44
44
  raise ArgumentError, "Invalid Certificate format" if parts.length != 8
45
45
 
46
46
  cid = parts[0].to_i(16)
47
- issued_at = parts[1].to_i
48
- issuance_duration = parts[2].to_i
49
- ttl = parts[3].to_i
50
- sig_algo = parts[4]
51
- cry_algo = parts[5]
52
- signature_key = Saro::Dat::DatSignature.imports(sig_algo, parts[6])
53
- crypto_key = Saro::Dat::DatCrypto.imports(cry_algo, parts[7])
47
+ dat_issuance_start_seconds = parts[1].to_i
48
+ dat_issuance_duration_seconds = parts[2].to_i
49
+ dat_ttl_seconds = parts[3].to_i
50
+ signature_algorithm = parts[4]
51
+ crypto_algorithm = parts[5]
52
+ signature_key = Saro::Dat::DatSignature.imports(signature_algorithm, parts[6])
53
+ crypto_key = Saro::Dat::DatCrypto.imports(crypto_algorithm, parts[7])
54
54
 
55
- new(cid, issued_at, issuance_duration, ttl, signature_key, crypto_key)
55
+ new(cid, dat_issuance_start_seconds, dat_issuance_duration_seconds, dat_ttl_seconds, signature_key, crypto_key)
56
56
  end
57
57
 
58
58
  def issuable
59
59
  now = Time.now.to_i
60
- signable && @dat_issue_begin <= now && now <= @dat_issue_end
60
+ signable && @dat_issuance_start_seconds <= now && now <= @dat_issuance_end_seconds
61
61
  end
62
62
 
63
63
  def expired
64
- Time.now.to_i > (@dat_issue_end + @dat_ttl)
64
+ Time.now.to_i > (@dat_issuance_end_seconds + @dat_ttl_seconds)
65
65
  end
66
66
 
67
67
  def signable
@@ -72,6 +72,10 @@ module Saro
72
72
  @signature_key.pair
73
73
  end
74
74
 
75
+ def support_verify_only
76
+ @signature_key.support_verify_only
77
+ end
78
+
75
79
  # For Ruby conventions
76
80
  alias_method :issuable?, :issuable
77
81
  alias_method :expired?, :expired
@@ -0,0 +1,187 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'net/http'
4
+ require 'uri'
5
+ require 'logger'
6
+ require 'thread'
7
+ require_relative 'dat_manager'
8
+ require_relative 'dat'
9
+
10
+ module Saro
11
+ module Dat
12
+ class DatCmsManager
13
+ DAT_CMS_API_VERSION = "v1"
14
+
15
+ def initialize(uri:, token:, interval_seconds: 60, verify_only: false, dat_manager: nil)
16
+ @uri = uri
17
+ @token = token
18
+ @interval_seconds = interval_seconds
19
+ @verify_only = verify_only
20
+ @manager = dat_manager || DatManager.new
21
+ @version = 0
22
+ @lock = Mutex.new
23
+ @stopped = false
24
+ @logger = Logger.new($stdout)
25
+ @logger.level = Logger::DEBUG
26
+
27
+ sync
28
+
29
+ if @interval_seconds > 0
30
+ schedule_sync
31
+ end
32
+ end
33
+
34
+ def stop
35
+ @lock.synchronize do
36
+ @stopped = true
37
+ @thread&.kill # 혹은 다른 방식으로 스레드 중지
38
+ end
39
+ end
40
+
41
+ def sync
42
+ # non-blocking lock
43
+ unless @lock.try_lock
44
+ @logger.warn("Last request ignored (Duplicate request)")
45
+ return
46
+ end
47
+
48
+ begin
49
+ url = URI("#{@uri}?version=#{@version}")
50
+ request = Net::HTTP::Get.new(url)
51
+ request["Authorization"] = @token
52
+
53
+ response = Net::HTTP.start(url.host, url.port, use_ssl: url.scheme == 'https', open_timeout: 10, read_timeout: 10) do |http|
54
+ http.request(request)
55
+ end
56
+
57
+ if response.code != "200"
58
+ @logger.error("Response status error, status:#{response.code} in #{url}")
59
+ return
60
+ end
61
+
62
+ body = response.body
63
+ if body.nil? || body.empty?
64
+ @logger.debug("No new certificate: #{url}")
65
+ return
66
+ end
67
+
68
+ lines = body.split("\n", 2)
69
+ if lines.length < 2
70
+ if body.start_with?("\n")
71
+ @logger.error("Invalid response: #{url}")
72
+ return
73
+ end
74
+ @logger.debug("No new certificate: #{url}")
75
+ return
76
+ end
77
+
78
+ new_version_str = lines[0].strip
79
+ new_certificates = lines[1].strip
80
+
81
+ if new_version_str.empty?
82
+ @logger.error("Invalid version in response: #{url}")
83
+ return
84
+ end
85
+
86
+ begin
87
+ new_version = Integer(new_version_str)
88
+ renew_count = @manager.imports(new_certificates, clear: false)
89
+ @version = new_version
90
+ @logger.debug("Renewed #{renew_count} certificates for version #{new_version}: #{url}")
91
+ rescue ArgumentError => e
92
+ @logger.error("Failed to parse version or certificates: #{e.message}")
93
+ end
94
+
95
+ rescue StandardError => e
96
+ @logger.error("[Exception] DAT CMS Sync #{@uri}: #{e.message}")
97
+ ensure
98
+ @lock.unlock
99
+ end
100
+ end
101
+
102
+ def get_manager
103
+ @manager
104
+ end
105
+
106
+ def issue(plain, secure)
107
+ @manager.issue(plain, secure)
108
+ end
109
+
110
+ def parse(dat)
111
+ @manager.parse(dat)
112
+ end
113
+
114
+ def self.builder
115
+ DatCmsManagerBuilder.new
116
+ end
117
+
118
+ private
119
+
120
+ def schedule_sync
121
+ @thread = Thread.new do
122
+ loop do
123
+ sleep(@interval_seconds)
124
+ break if @stopped
125
+ run_sync_task
126
+ end
127
+ end
128
+ end
129
+
130
+ def run_sync_task
131
+ sync
132
+ rescue StandardError => e
133
+ @logger.error("Error in sync task: #{e.message}")
134
+ end
135
+ end
136
+
137
+ class DatCmsManagerBuilder
138
+ def initialize
139
+ @uri = "http://localhost:8088"
140
+ @token = ""
141
+ @verify_only = false
142
+ @interval_seconds = 60
143
+ end
144
+
145
+ def uri(uri)
146
+ @uri = uri.delete_suffix('/')
147
+ self
148
+ end
149
+
150
+ def token(token)
151
+ @token = token
152
+ self
153
+ end
154
+
155
+ def verify_only(verify_only)
156
+ @verify_only = verify_only
157
+ self
158
+ end
159
+
160
+ def interval_seconds(interval_seconds)
161
+ @interval_seconds = interval_seconds
162
+ self
163
+ end
164
+
165
+ def build
166
+ parsed = URI.parse(@uri)
167
+
168
+ if parsed.path && parsed.path != '' && parsed.path != '/'
169
+ raise ArgumentError, "uri must be path-less: #{@uri}"
170
+ end
171
+ if parsed.query
172
+ raise ArgumentError, "uri must be query-less: #{@uri}"
173
+ end
174
+
175
+ path = @verify_only ? "/v1/certs/verify-only" : "/v1/certs"
176
+ final_uri = "#{parsed.scheme}://#{parsed.host}:#{parsed.port}#{path}"
177
+
178
+ DatCmsManager.new(
179
+ uri: final_uri,
180
+ token: @token,
181
+ interval_seconds: @interval_seconds,
182
+ verify_only: @verify_only
183
+ )
184
+ end
185
+ end
186
+ end
187
+ end
@@ -17,9 +17,10 @@ module Saro
17
17
  end
18
18
 
19
19
  def import_certificates(input_certs, clear: false)
20
+ renew_count = 0
20
21
  @lock.with_write_lock do
21
22
  certificates = clear ? [] : @certificates.dup
22
-
23
+
23
24
  before_cids = Set.new(certificates.map(&:cid))
24
25
  seen_cids = Set.new
25
26
 
@@ -30,9 +31,10 @@ module Saro
30
31
  next if before_cids.include?(cert.cid)
31
32
 
32
33
  certificates << cert
34
+ renew_count += 1
33
35
  end
34
36
 
35
- certificates.sort_by!(&:dat_issue_end)
37
+ certificates.sort_by!(&:dat_issuance_end_seconds)
36
38
 
37
39
  # Find latest issuable certificate as issuer
38
40
  issuer = certificates.reverse_each.find(&:issuable)
@@ -40,6 +42,7 @@ module Saro
40
42
  @issuer = issuer
41
43
  @certificates = certificates
42
44
  end
45
+ renew_count
43
46
  end
44
47
 
45
48
  def imports(format_str, clear: false)
@@ -82,7 +85,7 @@ module Saro
82
85
 
83
86
  def self._issue(cert, plain, secure)
84
87
  now = Time.now.to_i
85
- expire = now + cert.dat_ttl
88
+ expire = now + cert.dat_ttl_seconds
86
89
  cid_hex = cert.cid.to_s(16)
87
90
 
88
91
  plain_bytes = plain.is_a?(String) ? plain.encode('utf-8') : (plain || "".b)
@@ -114,6 +114,10 @@ module Saro
114
114
  end
115
115
 
116
116
  def exports(verify_only = false)
117
+ if verify_only && !support_verify_only
118
+ raise ArgumentError, "#{config[:name]} does not supported verifying only key"
119
+ end
120
+
117
121
  if @config[:name] == "HMAC"
118
122
  Saro::Dat::Util.encode_base64_url_str(@verifying_key)
119
123
  else
@@ -182,6 +186,10 @@ module Saro
182
186
  @config[:name] == "ECDSA"
183
187
  end
184
188
 
189
+ def support_verify_only
190
+ @config[:name] == "ECDSA"
191
+ end
192
+
185
193
  private
186
194
 
187
195
  def der_to_raw_signature(signature_der)
data/lib/saro-dat.rb CHANGED
@@ -6,6 +6,7 @@ require_relative 'saro/dat/signature'
6
6
  require_relative 'saro/dat/dat_certificate'
7
7
  require_relative 'saro/dat/dat'
8
8
  require_relative 'saro/dat/dat_manager'
9
+ require_relative 'saro/dat/dat_cms_manager'
9
10
 
10
11
  module Saro
11
12
  module Dat
data/saro-dat.gemspec CHANGED
@@ -2,13 +2,13 @@
2
2
 
3
3
  Gem::Specification.new do |spec|
4
4
  spec.name = "saro-dat"
5
- spec.version = "4.0.0"
5
+ spec.version = "4.3.1"
6
6
  spec.authors = ["marker"]
7
7
  spec.email = ["j@saro.me"]
8
8
 
9
9
  spec.summary = "DAT (Data Access Token) Ruby implementation"
10
10
  spec.description = "Ported from Python dat library"
11
- spec.homepage = "https://dat.saro.me/--/libs/gems-saro-dat"
11
+ spec.homepage = "https://dat.saro.me/libs/gems-saro-dat"
12
12
  spec.license = "MIT"
13
13
  spec.required_ruby_version = ">= 2.7.0"
14
14
 
@@ -30,6 +30,7 @@ Gem::Specification.new do |spec|
30
30
  spec.add_dependency "concurrent-ruby", "~> 1.3.6"
31
31
  spec.add_dependency "openssl", "~> 4.0.2"
32
32
  spec.add_dependency "base64"
33
+ spec.add_dependency "logger"
33
34
 
34
35
  spec.add_development_dependency "minitest", "~> 5.0"
35
36
  spec.add_development_dependency "benchmark"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: saro-dat
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.0
4
+ version: 4.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - marker
@@ -51,6 +51,20 @@ dependencies:
51
51
  - - ">="
52
52
  - !ruby/object:Gem::Version
53
53
  version: '0'
54
+ - !ruby/object:Gem::Dependency
55
+ name: logger
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ type: :runtime
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
54
68
  - !ruby/object:Gem::Dependency
55
69
  name: minitest
56
70
  requirement: !ruby/object:Gem::Requirement
@@ -113,15 +127,16 @@ files:
113
127
  - lib/saro/dat/crypto.rb
114
128
  - lib/saro/dat/dat.rb
115
129
  - lib/saro/dat/dat_certificate.rb
130
+ - lib/saro/dat/dat_cms_manager.rb
116
131
  - lib/saro/dat/dat_manager.rb
117
132
  - lib/saro/dat/signature.rb
118
133
  - lib/saro/dat/util.rb
119
134
  - saro-dat.gemspec
120
- homepage: https://dat.saro.me/--/libs/gems-saro-dat
135
+ homepage: https://dat.saro.me/libs/gems-saro-dat
121
136
  licenses:
122
137
  - MIT
123
138
  metadata:
124
- homepage_uri: https://dat.saro.me/--/libs/gems-saro-dat
139
+ homepage_uri: https://dat.saro.me/libs/gems-saro-dat
125
140
  source_code_uri: https://github.com/saro-lab/dat-ruby
126
141
  changelog_uri: https://github.com/saro-lab/dat-ruby/blob/main/CHANGELOG.md
127
142
  keywords: dat, distributed, access, token, web, session, security, authentication