didkit 0.2.1 → 0.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e1dfc5eee5f96a374d8f67151f8d44f5eafffc8a8e3624d7fe507e783d5ff814
4
- data.tar.gz: 74f0cd8da39a5e723b806e07560bd15325d2176c93ca746d619975e6b0510c77
3
+ metadata.gz: ae1ff103a9695991ae3e0e97072c2b10731e7c18e44cfa5f715995ef0631df4f
4
+ data.tar.gz: b2bc822873a7804515d3ad06caa6c15e4177943668f6bcbb79f8c48fa87e4733
5
5
  SHA512:
6
- metadata.gz: 976f7d9a63ad453dde3064872dafb558cbc80f96db0a61d5243abb68664b360a38ede6cfe5a702df7609b60c6d7c6e917ffc1a0479351bb89c7c693af5ffb3c7
7
- data.tar.gz: da1e08939b8c19d62ceef10bd48d278b7190fffdbcad65385f9ca36edd9377d0ee37d1b1d78e8ae963304dd06863cdcf9fa992a504b3e219c1f01dbf60248f6a
6
+ metadata.gz: 5e4bbd991480a0a98c13c514e52a65bdceafd1c1a4feb75d60db07b22af10f376edaaea89bd98122c94c902c718c241a01965abaa801433b2a48162bb86d6ec1
7
+ data.tar.gz: 791481ed39520a72eb750e2d511f6017ca476a1386c15d6d6a667e2a170a7e63d0723e868b2d843c87d66d3d33306fcc6bd5cd3d359cbea8e213e6ba8a401f99
data/CHANGELOG.md CHANGED
@@ -1,3 +1,14 @@
1
+ ## [0.2.3] - 2024-07-02
2
+
3
+ - added a `DID#get_audit_log` method that fetches the PLC audit log for a DID
4
+ - added a way to set an error handler in `PLCImporter`
5
+ - reverted the change from 0.2.1 that added Ruby stdlib dependencies explicitly to the gemspec, since this causes more problems than it's worth
6
+ - minor bug fixes
7
+
8
+ ## [0.2.2] - 2024-04-01
9
+
10
+ - added helpers for checking if a DID is known by (federated with) a relay or if the repo exists on its assigned PDS
11
+
1
12
  ## [0.2.1] - 2024-03-26
2
13
 
3
14
  - tweaked validations in `Document` and `PLCOperation` to make them more aligned with what might be expected
data/README.md CHANGED
@@ -1,6 +1,9 @@
1
- # DIDKit
1
+ # DIDKit 🪪
2
2
 
3
- A small Ruby gem for handling Distributed Identifiers (DIDs) in Bluesky / AT Protocol
3
+ A small Ruby gem for handling Distributed Identifiers (DIDs) in Bluesky / AT Protocol.
4
+
5
+ > [!NOTE]
6
+ > ATProto Ruby gems collection: [skyfall](https://github.com/mackuba/skyfall) | [blue_factory](https://github.com/mackuba/blue_factory) | [minisky](https://github.com/mackuba/minisky) | [didkit](https://github.com/mackuba/didkit)
4
7
 
5
8
 
6
9
  ## What does it do
data/lib/didkit/did.rb CHANGED
@@ -1,8 +1,11 @@
1
1
  require_relative 'errors'
2
+ require_relative 'requests'
2
3
  require_relative 'resolver'
3
4
 
4
5
  module DIDKit
5
6
  class DID
7
+ include Requests
8
+
6
9
  def self.resolve_handle(handle)
7
10
  Resolver.new.resolve_handle(handle)
8
11
  end
@@ -34,10 +37,46 @@ module DIDKit
34
37
  Resolver.new.get_validated_handle(self)
35
38
  end
36
39
 
40
+ def get_audit_log
41
+ if @type == :plc
42
+ PLCImporter.new.fetch_audit_log(self)
43
+ else
44
+ raise DIDError.new("Audit log not supported for did:#{@type}")
45
+ end
46
+ end
47
+
37
48
  def web_domain
38
49
  did.gsub(/^did\:web\:/, '') if type == :web
39
50
  end
40
51
 
52
+ def is_known_by_relay?(relay, options = {})
53
+ relay_host = relay.include?('://') ? URI(relay).origin : "https://#{relay}"
54
+ url = URI("#{relay_host}/xrpc/com.atproto.sync.getLatestCommit")
55
+ url.query = URI.encode_www_form(:did => did)
56
+
57
+ response = get_response(url, { timeout: 30, max_redirects: 5 }.merge(options))
58
+ status = response.code.to_i
59
+ is_json = (response['Content-Type'] =~ /^application\/json(;.*)?$/)
60
+
61
+ if status == 200
62
+ true
63
+ elsif status == 400 && is_json && JSON.parse(response.body)['error'] == 'RepoNotFound'
64
+ false
65
+ elsif status == 404 && is_json && JSON.parse(response.body)['error']
66
+ false
67
+ else
68
+ raise APIError.new(response)
69
+ end
70
+ end
71
+
72
+ def account_exists?
73
+ doc = get_document
74
+ return false if doc.pds_endpoint.nil?
75
+
76
+ pds_host = URI(doc.pds_endpoint).origin
77
+ is_known_by_relay?(pds_host, timeout: 10)
78
+ end
79
+
41
80
  def ==(other)
42
81
  if other.is_a?(String)
43
82
  self.did == other
data/lib/didkit/errors.rb CHANGED
@@ -1,4 +1,21 @@
1
1
  module DIDKit
2
2
  class DIDError < StandardError
3
3
  end
4
+
5
+ class APIError < StandardError
6
+ attr_reader :response
7
+
8
+ def initialize(response)
9
+ @response = response
10
+ super("APIError: #{response}")
11
+ end
12
+
13
+ def status
14
+ response.code.to_i
15
+ end
16
+
17
+ def body
18
+ response.body
19
+ end
20
+ end
4
21
  end
@@ -9,7 +9,7 @@ module DIDKit
9
9
  PLC_SERVICE = 'plc.directory'
10
10
  MAX_PAGE = 1000
11
11
 
12
- attr_accessor :ignore_errors, :last_date
12
+ attr_accessor :ignore_errors, :last_date, :error_handler
13
13
 
14
14
  def initialize(since: nil)
15
15
  if since.to_s == 'beginning'
@@ -22,14 +22,22 @@ module DIDKit
22
22
  @last_date = Time.now
23
23
  @eof = true
24
24
  end
25
-
26
- @ignore_errors = false
27
25
  end
28
26
 
29
27
  def plc_service
30
28
  PLC_SERVICE
31
29
  end
32
30
 
31
+ def ignore_errors=(val)
32
+ @ignore_errors = val
33
+
34
+ if val
35
+ @error_handler = proc { |e, j| "(ignore error)" }
36
+ else
37
+ @error_handler = nil
38
+ end
39
+ end
40
+
33
41
  def get_export(args = {})
34
42
  url = URI("https://#{plc_service}/export")
35
43
  url.query = URI.encode_www_form(args)
@@ -38,6 +46,11 @@ module DIDKit
38
46
  data.lines.map(&:strip).reject(&:empty?).map { |x| JSON.parse(x) }
39
47
  end
40
48
 
49
+ def fetch_audit_log(did)
50
+ response = URI.open("https://#{plc_service}/#{did}/log/audit").read
51
+ JSON.parse(response).map { |j| PLCOperation.new(j) }
52
+ end
53
+
41
54
  def fetch_page
42
55
  request_time = Time.now
43
56
 
@@ -47,8 +60,9 @@ module DIDKit
47
60
  operations = rows.filter_map do |json|
48
61
  begin
49
62
  PLCOperation.new(json)
50
- rescue PLCOperation::FormatError => e
51
- ignore_errors ? nil : raise
63
+ rescue PLCOperation::FormatError, AtHandles::FormatError, ServiceRecord::FormatError => e
64
+ @error_handler ? @error_handler.call(e, json) : raise
65
+ nil
52
66
  end
53
67
  end
54
68
 
@@ -0,0 +1,28 @@
1
+ module DIDKit::Requests
2
+ def get_response(url, options = {})
3
+ url = URI(url) unless url.is_a?(URI)
4
+ request_options = { use_ssl: true }
5
+
6
+ if timeout = options[:timeout]
7
+ request_options[:open_timeout] = timeout
8
+ request_options[:read_timeout] = timeout
9
+ end
10
+
11
+ redirects = 0
12
+ max_redirects = options[:max_redirects] || 0
13
+
14
+ loop do
15
+ response = Net::HTTP.start(url.host, url.port, request_options) do |http|
16
+ request = Net::HTTP::Get.new(url)
17
+ http.request(request)
18
+ end
19
+
20
+ if response.is_a?(Net::HTTPRedirection) && redirects < max_redirects && (location = response['Location'])
21
+ url = URI(location.include?('://') ? location : (url.origin + location))
22
+ redirects += 1
23
+ else
24
+ return response
25
+ end
26
+ end
27
+ end
28
+ end
@@ -5,12 +5,15 @@ require 'resolv'
5
5
 
6
6
  require_relative 'did'
7
7
  require_relative 'document'
8
+ require_relative 'requests'
8
9
 
9
10
  module DIDKit
10
11
  class Resolver
11
12
  RESERVED_DOMAINS = %w(alt arpa example internal invalid local localhost onion test)
12
13
  MAX_REDIRECTS = 5
13
14
 
15
+ include Requests
16
+
14
17
  attr_accessor :nameserver
15
18
 
16
19
  def initialize(options = {})
@@ -32,9 +35,9 @@ module DIDKit
32
35
  end
33
36
 
34
37
  def resolve_handle_by_dns(domain)
35
- dns_records = Resolv::DNS.open(resolv_options) { |d|
38
+ dns_records = Resolv::DNS.open(resolv_options) do |d|
36
39
  d.getresources("_atproto.#{domain}", Resolv::DNS::Resource::IN::TXT)
37
- }
40
+ end
38
41
 
39
42
  if record = dns_records.first
40
43
  if string = record.strings.first
@@ -46,26 +49,11 @@ module DIDKit
46
49
  end
47
50
 
48
51
  def resolve_handle_by_well_known(domain)
49
- resolve_handle_from_url("https://#{domain}/.well-known/atproto-did")
50
- end
51
-
52
- def resolve_handle_from_url(url, redirects = 0)
53
- url = URI(url) unless url.is_a?(URI)
52
+ url = "https://#{domain}/.well-known/atproto-did"
53
+ response = get_response(url, timeout: 10, max_redirects: MAX_REDIRECTS)
54
54
 
55
- response = Net::HTTP.start(url.host, url.port, use_ssl: true, open_timeout: 10, read_timeout: 10) do |http|
56
- request = Net::HTTP::Get.new(url)
57
- http.request(request)
58
- end
59
-
60
- if response.is_a?(Net::HTTPSuccess)
61
- if text = response.body
62
- return parse_did_from_well_known(text)
63
- end
64
- elsif response.is_a?(Net::HTTPRedirection) && redirects < MAX_REDIRECTS
65
- if location = response['Location']
66
- target_url = location.include?('://') ? location : (url.origin + location)
67
- return resolve_handle_from_url(target_url, redirects + 1)
68
- end
55
+ if response.is_a?(Net::HTTPSuccess) && (text = response.body)
56
+ return parse_did_from_well_known(text)
69
57
  end
70
58
 
71
59
  nil
@@ -3,6 +3,9 @@ require_relative 'errors'
3
3
 
4
4
  module DIDKit
5
5
  class ServiceRecord
6
+ class FormatError < StandardError
7
+ end
8
+
6
9
  attr_reader :key, :type, :endpoint
7
10
 
8
11
  def initialize(key, type, endpoint)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DIDKit
4
- VERSION = "0.2.1"
4
+ VERSION = "0.2.3"
5
5
  end
metadata CHANGED
@@ -1,99 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: didkit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kuba Suder
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-03-26 00:00:00.000000000 Z
12
- dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: json
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: '2.5'
20
- type: :runtime
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - "~>"
25
- - !ruby/object:Gem::Version
26
- version: '2.5'
27
- - !ruby/object:Gem::Dependency
28
- name: net-http
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - "~>"
32
- - !ruby/object:Gem::Version
33
- version: '0.1'
34
- type: :runtime
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - "~>"
39
- - !ruby/object:Gem::Version
40
- version: '0.1'
41
- - !ruby/object:Gem::Dependency
42
- name: open-uri
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - "~>"
46
- - !ruby/object:Gem::Version
47
- version: '0.1'
48
- type: :runtime
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - "~>"
53
- - !ruby/object:Gem::Version
54
- version: '0.1'
55
- - !ruby/object:Gem::Dependency
56
- name: resolv
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - "~>"
60
- - !ruby/object:Gem::Version
61
- version: '0.1'
62
- type: :runtime
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - "~>"
67
- - !ruby/object:Gem::Version
68
- version: '0.1'
69
- - !ruby/object:Gem::Dependency
70
- name: time
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - "~>"
74
- - !ruby/object:Gem::Version
75
- version: '0.3'
76
- type: :runtime
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - "~>"
81
- - !ruby/object:Gem::Version
82
- version: '0.3'
83
- - !ruby/object:Gem::Dependency
84
- name: uri
85
- requirement: !ruby/object:Gem::Requirement
86
- requirements:
87
- - - "~>"
88
- - !ruby/object:Gem::Version
89
- version: '0.13'
90
- type: :runtime
91
- prerelease: false
92
- version_requirements: !ruby/object:Gem::Requirement
93
- requirements:
94
- - - "~>"
95
- - !ruby/object:Gem::Version
96
- version: '0.13'
11
+ date: 2024-07-02 00:00:00.000000000 Z
12
+ dependencies: []
97
13
  description:
98
14
  email:
99
15
  - jakub.suder@gmail.com
@@ -111,6 +27,7 @@ files:
111
27
  - lib/didkit/errors.rb
112
28
  - lib/didkit/plc_importer.rb
113
29
  - lib/didkit/plc_operation.rb
30
+ - lib/didkit/requests.rb
114
31
  - lib/didkit/resolver.rb
115
32
  - lib/didkit/service_record.rb
116
33
  - lib/didkit/services.rb