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 +4 -4
- data/CHANGELOG.md +11 -0
- data/README.md +5 -2
- data/lib/didkit/did.rb +39 -0
- data/lib/didkit/errors.rb +17 -0
- data/lib/didkit/plc_importer.rb +19 -5
- data/lib/didkit/requests.rb +28 -0
- data/lib/didkit/resolver.rb +9 -21
- data/lib/didkit/service_record.rb +3 -0
- data/lib/didkit/version.rb +1 -1
- metadata +4 -87
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ae1ff103a9695991ae3e0e97072c2b10731e7c18e44cfa5f715995ef0631df4f
|
4
|
+
data.tar.gz: b2bc822873a7804515d3ad06caa6c15e4177943668f6bcbb79f8c48fa87e4733
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
data/lib/didkit/plc_importer.rb
CHANGED
@@ -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
|
-
|
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
|
data/lib/didkit/resolver.rb
CHANGED
@@ -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)
|
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
|
-
|
50
|
-
|
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
|
56
|
-
|
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
|
data/lib/didkit/version.rb
CHANGED
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.
|
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-
|
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
|