didkit 0.0.3 → 0.1.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 +4 -4
- data/CHANGELOG.md +15 -0
- data/README.md +67 -5
- data/lib/didkit/did.rb +15 -57
- data/lib/didkit/document.rb +7 -1
- data/lib/didkit/plc_importer.rb +45 -0
- data/lib/didkit/plc_operation.rb +56 -0
- data/lib/didkit/resolver.rb +115 -0
- data/lib/didkit/version.rb +1 -1
- metadata +5 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7744ae1dc9ba4ca678d641139ffa94931130ee7f91fa93d3ee89e3d6e196ba8d
|
|
4
|
+
data.tar.gz: 4111e824027b646aa8b3a21fb3032dfdba26019b6fecaf3f40f2120dfa417e52
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ed96e325c2d0e8152b1db1f6a46e34afdbec049e0442f2298a89a2bbd07afb96e3ca166fca40adf1ff90d0da02997b825addd20163eb858c6074771d9ea229ce
|
|
7
|
+
data.tar.gz: 9237183a427ad4f5380504334abcfb16a965b0e95ce39693e9aa8d58d24c6acc6c00d7150bd86be18332e02da3cbd379eb516ccb60716a3b09bf11efc155c26c
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
## [0.1.0] - 2024-03-12
|
|
2
|
+
|
|
3
|
+
- rejecting handles from disallowed domains like `.arpa` or `.test`
|
|
4
|
+
- validating handles with the `.well-known` file having a trailing newline
|
|
5
|
+
- validating handles with `.well-known` address returning a redirect
|
|
6
|
+
- added `#pick_valid_handle` helper
|
|
7
|
+
- allow overriding the nameserver for `Resolv::DNS`
|
|
8
|
+
- other bug fixes
|
|
9
|
+
|
|
10
|
+
## [0.0.4] - 2024-03-07
|
|
11
|
+
|
|
12
|
+
- extracted resolving code from `DID` to a new `Resolver` class (`DID` has helper methods to call the resolver)
|
|
13
|
+
- added `Resolver#get_validated_handle` method to validate handles from the `Document` (+ helpers in `DID` in `Document`)
|
|
14
|
+
- added timeout to `#resolve_handle_by_well_known`
|
|
15
|
+
|
|
1
16
|
## [0.0.3] - 2024-03-06
|
|
2
17
|
|
|
3
18
|
- added `Document#handles` with handle info extracted from `alsoKnownAs` field
|
data/README.md
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
#
|
|
1
|
+
# DIDKit
|
|
2
2
|
|
|
3
3
|
A small Ruby gem for handling Distributed Identifiers (DIDs) in Bluesky / AT Protocol
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
## What does it do
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
See the [did.rb](https://github.com/mackuba/didkit/blob/master/lib/didkit/did.rb) file for now.
|
|
8
|
+
Accounts on Bluesky use identifiers like [did:plc:oio4hkxaop4ao4wz2pp3f4cr](https://plc.directory/did:plc:oio4hkxaop4ao4wz2pp3f4cr) as unique IDs, and they also have assigned human-readable handles like [@mackuba.eu](https://bsky.app/profile/mackuba.eu), which are verified either through a DNS TXT entry or a `/.well-known/atproto-did` file. This library allows you to look up any account's assigned handle using a DID string or vice versa, load the account's DID JSON document that specifies the handles and the PDS server hosting user's repo, and check if the assigned handle verifies correctly.
|
|
11
9
|
|
|
12
10
|
|
|
13
11
|
## Installation
|
|
@@ -15,8 +13,72 @@ See the [did.rb](https://github.com/mackuba/didkit/blob/master/lib/didkit/did.rb
|
|
|
15
13
|
gem install didkit
|
|
16
14
|
|
|
17
15
|
|
|
16
|
+
## Usage
|
|
17
|
+
|
|
18
|
+
Use the `DIDKit::Resolver` class to look up DIDs and handles.
|
|
19
|
+
|
|
20
|
+
To look up a handle:
|
|
21
|
+
|
|
22
|
+
```rb
|
|
23
|
+
resolver = DIDKit::Resolver.new
|
|
24
|
+
resolver.resolve_handle('nytimes.com')
|
|
25
|
+
# => #<DIDKit::DID:0x00000001035956b0 @did="did:plc:eclio37ymobqex2ncko63h4r", @type=:plc, @resolved_by=:dns>
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
This returns an object of `DIDKit::DID` class (aliased as just `DID`), which tells you:
|
|
29
|
+
|
|
30
|
+
- the DID as a string (`#to_s` or `#did`)
|
|
31
|
+
- the DID type (`#type`, `:plc` or `:web`)
|
|
32
|
+
- if the handle was resolved via a DNS entry or a `.well-known` file (`#resolved_by`, `:dns` or `:http`)
|
|
33
|
+
|
|
34
|
+
To go in the other direction – to find an assigned and verified handle given a DID – use `get_validated_handle` (pass DID as a string or an object):
|
|
35
|
+
|
|
36
|
+
```rb
|
|
37
|
+
resolver.get_validated_handle('did:plc:ewvi7nxzyoun6zhxrhs64oiz')
|
|
38
|
+
# => "atproto.com"
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
You can also load the DID document using `resolve_did`:
|
|
42
|
+
|
|
43
|
+
```rb
|
|
44
|
+
doc = resolver.resolve_did('did:plc:ragtjsm2j2vknwkz3zp4oxrd')
|
|
45
|
+
# => #<DIDKit::Document:0x0000000105d751f8 @did=#<DIDKit::DID:...>, @json={...}>
|
|
46
|
+
|
|
47
|
+
doc.handles
|
|
48
|
+
# => ["pfrazee.com"]
|
|
49
|
+
|
|
50
|
+
doc.pds_endpoint
|
|
51
|
+
# => "https://morel.us-east.host.bsky.network"
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
There are also some helper methods in the `DID` class that create a `Resolver` for you to save you some typing:
|
|
55
|
+
|
|
56
|
+
```rb
|
|
57
|
+
did = DID.resolve_handle('jay.bsky.team')
|
|
58
|
+
# => #<DIDKit::DID:0x000000010615ed28 @did="did:plc:oky5czdrnfjpqslsw2a5iclo", @type=:plc, @resolved_by=:dns>
|
|
59
|
+
|
|
60
|
+
did.to_s
|
|
61
|
+
# => "did:plc:oky5czdrnfjpqslsw2a5iclo"
|
|
62
|
+
|
|
63
|
+
did.get_document
|
|
64
|
+
# => #<DIDKit::Document:0x00000001066d4898 @did=#<DIDKit::DID:...>, @json={...}>
|
|
65
|
+
|
|
66
|
+
did.get_validated_handle
|
|
67
|
+
# => "jay.bsky.team"
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
### Configuration
|
|
72
|
+
|
|
73
|
+
You can override the nameserver used for DNS lookups by setting the `nameserver` property in `Resolver`, e.g. to use Google's or CloudFlare's global DNS:
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
resolver.nameserver = '8.8.8.8'
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
|
|
18
80
|
## Credits
|
|
19
81
|
|
|
20
|
-
Copyright ©
|
|
82
|
+
Copyright © 2024 Kuba Suder ([@mackuba.eu](https://bsky.app/profile/mackuba.eu)).
|
|
21
83
|
|
|
22
84
|
The code is available under the terms of the [zlib license](https://choosealicense.com/licenses/zlib/) (permissive, similar to MIT).
|
data/lib/didkit/did.rb
CHANGED
|
@@ -1,54 +1,10 @@
|
|
|
1
|
-
require 'json'
|
|
2
|
-
require 'net/http'
|
|
3
|
-
require 'open-uri'
|
|
4
|
-
require 'resolv'
|
|
5
|
-
|
|
6
|
-
require_relative 'document'
|
|
7
1
|
require_relative 'errors'
|
|
2
|
+
require_relative 'resolver'
|
|
8
3
|
|
|
9
4
|
module DIDKit
|
|
10
5
|
class DID
|
|
11
6
|
def self.resolve_handle(handle)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
if dns_did = resolve_handle_by_dns(domain)
|
|
15
|
-
DID.new(dns_did, :dns)
|
|
16
|
-
elsif http_did = resolve_handle_by_well_known(domain)
|
|
17
|
-
DID.new(http_did, :http)
|
|
18
|
-
else
|
|
19
|
-
nil
|
|
20
|
-
end
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def self.resolve_handle_by_dns(domain)
|
|
24
|
-
dns_records = Resolv::DNS.open { |d| d.getresources("_atproto.#{domain}", Resolv::DNS::Resource::IN::TXT) }
|
|
25
|
-
|
|
26
|
-
if record = dns_records.first
|
|
27
|
-
if string = record.strings.first
|
|
28
|
-
if string =~ /^did\=(did\:\w+\:.*)$/
|
|
29
|
-
return $1
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
nil
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
def self.resolve_handle_by_well_known(domain)
|
|
38
|
-
url = URI("https://#{domain}/.well-known/atproto-did")
|
|
39
|
-
response = Net::HTTP.get_response(url)
|
|
40
|
-
|
|
41
|
-
if response.is_a?(Net::HTTPSuccess)
|
|
42
|
-
if text = response.body
|
|
43
|
-
if text.lines.length == 1 && text.start_with?('did:')
|
|
44
|
-
return text
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
nil
|
|
50
|
-
rescue StandardError => e
|
|
51
|
-
nil
|
|
7
|
+
Resolver.new.resolve_handle(handle)
|
|
52
8
|
end
|
|
53
9
|
|
|
54
10
|
attr_reader :type, :did, :resolved_by
|
|
@@ -71,23 +27,25 @@ module DIDKit
|
|
|
71
27
|
alias to_s did
|
|
72
28
|
|
|
73
29
|
def get_document
|
|
74
|
-
|
|
30
|
+
Resolver.new.resolve_did(self)
|
|
75
31
|
end
|
|
76
32
|
|
|
77
|
-
def
|
|
78
|
-
|
|
33
|
+
def get_validated_handle
|
|
34
|
+
Resolver.new.get_validated_handle(self)
|
|
79
35
|
end
|
|
80
36
|
|
|
81
|
-
def
|
|
82
|
-
|
|
83
|
-
json = JSON.parse(URI.open(url).read)
|
|
84
|
-
Document.new(self, json)
|
|
37
|
+
def web_domain
|
|
38
|
+
did.gsub(/^did\:web\:/, '') if type == :web
|
|
85
39
|
end
|
|
86
40
|
|
|
87
|
-
def
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
41
|
+
def ==(other)
|
|
42
|
+
if other.is_a?(String)
|
|
43
|
+
self.did == other
|
|
44
|
+
elsif other.is_a?(DID)
|
|
45
|
+
self.did == other.did
|
|
46
|
+
else
|
|
47
|
+
false
|
|
48
|
+
end
|
|
91
49
|
end
|
|
92
50
|
end
|
|
93
51
|
end
|
data/lib/didkit/document.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
require_relative 'resolver'
|
|
2
|
+
|
|
1
3
|
module DIDKit
|
|
2
4
|
class Document
|
|
3
5
|
class FormatError < StandardError
|
|
@@ -8,7 +10,7 @@ module DIDKit
|
|
|
8
10
|
def initialize(did, json)
|
|
9
11
|
raise FormatError, "Missing id field" if json['id'].nil?
|
|
10
12
|
raise FormatError, "Invalid id field" unless json['id'].is_a?(String)
|
|
11
|
-
raise FormatError, "
|
|
13
|
+
raise FormatError, "id field doesn't match expected DID" unless json['id'] == did.to_s
|
|
12
14
|
|
|
13
15
|
@did = did
|
|
14
16
|
@json = json
|
|
@@ -37,5 +39,9 @@ module DIDKit
|
|
|
37
39
|
@handles = []
|
|
38
40
|
end
|
|
39
41
|
end
|
|
42
|
+
|
|
43
|
+
def get_validated_handle
|
|
44
|
+
Resolver.new.pick_valid_handle(did, handles)
|
|
45
|
+
end
|
|
40
46
|
end
|
|
41
47
|
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
require 'json'
|
|
2
|
+
require 'net/http'
|
|
3
|
+
require 'open-uri'
|
|
4
|
+
require 'time'
|
|
5
|
+
|
|
6
|
+
require_relative 'plc_operation'
|
|
7
|
+
|
|
8
|
+
module DIDKit
|
|
9
|
+
class PLCImporter
|
|
10
|
+
PLC_SERVICE = 'plc.directory'
|
|
11
|
+
|
|
12
|
+
attr_accessor :ignore_errors
|
|
13
|
+
|
|
14
|
+
def initialize(last_date = Time.now)
|
|
15
|
+
@last_date = last_date
|
|
16
|
+
@ignore_errors = false
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def fetch
|
|
20
|
+
url = URI("https://#{PLC_SERVICE}/export")
|
|
21
|
+
url.query = URI.encode_www_form(:after => @last_date.utc.iso8601(6)) if @last_date
|
|
22
|
+
request_time = Time.now
|
|
23
|
+
|
|
24
|
+
data = URI.open(url).read
|
|
25
|
+
rows = data.lines.map(&:strip).reject(&:empty?).map { |x| JSON.parse(x) }
|
|
26
|
+
|
|
27
|
+
operations = rows.filter_map do |json|
|
|
28
|
+
begin
|
|
29
|
+
PLCOperation.new(json)
|
|
30
|
+
rescue PLCOperation::FormatError => e
|
|
31
|
+
ignore_errors ? nil : raise
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
block.call(operations)
|
|
36
|
+
|
|
37
|
+
if rows.length == 1000
|
|
38
|
+
@last_date = operations.last.created_at || request_time
|
|
39
|
+
run_update
|
|
40
|
+
else
|
|
41
|
+
@last_date = request_time
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
require 'time'
|
|
2
|
+
|
|
3
|
+
module DIDKit
|
|
4
|
+
class PLCOperation
|
|
5
|
+
class FormatError < StandardError
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
attr_reader :did, :created_at, :type, :pds_endpoint, :handles
|
|
9
|
+
|
|
10
|
+
def initialize(json)
|
|
11
|
+
@did = json['did']
|
|
12
|
+
raise FormatError, "Missing DID" if @did.nil?
|
|
13
|
+
raise FormatError, "Invalid DID" unless @did.is_a?(String) && @did.start_with?('did:')
|
|
14
|
+
|
|
15
|
+
timestamp = json['createdAt']
|
|
16
|
+
raise FormatError, "Missing createdAt" if timestamp.nil?
|
|
17
|
+
raise FormatError, "Invalid createdAt" unless timestamp.is_a?(String)
|
|
18
|
+
|
|
19
|
+
@created_at = Time.parse(timestamp)
|
|
20
|
+
|
|
21
|
+
operation = json['operation']
|
|
22
|
+
raise FormatError, "Missing operation key" if operation.nil?
|
|
23
|
+
raise FormatError, "Invalid operation data" unless operation.is_a?(Hash)
|
|
24
|
+
|
|
25
|
+
type = operation['type']
|
|
26
|
+
raise FormatError, "Missing type" if type.nil?
|
|
27
|
+
|
|
28
|
+
@type = type.to_sym
|
|
29
|
+
return unless @type == :plc_operation
|
|
30
|
+
|
|
31
|
+
services = operation['services']
|
|
32
|
+
raise FormatError, "Missing services key" if services.nil?
|
|
33
|
+
raise FormatError, "Invalid services data" unless services.is_a?(Hash)
|
|
34
|
+
|
|
35
|
+
if pds = services['atproto_pds']
|
|
36
|
+
raise FormatError, "Invalid PDS data" unless pds.is_a?(Hash)
|
|
37
|
+
raise FormatError, "Missing PDS type" unless pds['type']
|
|
38
|
+
raise FormatError, "Invalid PDS type" unless pds['type'] == 'AtprotoPersonalDataServer'
|
|
39
|
+
raise FormatError, "Missing PDS endpoint" unless pds['endpoint']
|
|
40
|
+
raise FormatError, "Invalid PDS endpoint" unless pds['endpoint'].is_a?(String) && pds['endpoint'] =~ %r(://)
|
|
41
|
+
|
|
42
|
+
@pds_endpoint = pds['endpoint']
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
if aka = operation['alsoKnownAs']
|
|
46
|
+
raise FormatError, "Invalid alsoKnownAs" unless aka.is_a?(Array)
|
|
47
|
+
raise FormatError, "Invalid alsoKnownAs" unless aka.all? { |x| x.is_a?(String) }
|
|
48
|
+
raise FormatError, "Invalid alsoKnownAs" unless aka.all? { |x| x =~ %r(\Aat://[^/]+\z) }
|
|
49
|
+
|
|
50
|
+
@handles = aka.map { |x| x.gsub('at://', '') }
|
|
51
|
+
else
|
|
52
|
+
@handles = []
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
require 'json'
|
|
2
|
+
require 'open-uri'
|
|
3
|
+
require 'net/http'
|
|
4
|
+
require 'resolv'
|
|
5
|
+
|
|
6
|
+
require_relative 'did'
|
|
7
|
+
require_relative 'document'
|
|
8
|
+
|
|
9
|
+
module DIDKit
|
|
10
|
+
class Resolver
|
|
11
|
+
RESERVED_DOMAINS = %w(alt arpa example internal invalid local localhost onion test)
|
|
12
|
+
MAX_REDIRECTS = 5
|
|
13
|
+
|
|
14
|
+
attr_accessor :nameserver
|
|
15
|
+
|
|
16
|
+
def resolve_handle(handle)
|
|
17
|
+
domain = handle.gsub(/^@/, '')
|
|
18
|
+
|
|
19
|
+
return nil if RESERVED_DOMAINS.include?(domain.split('.').last)
|
|
20
|
+
|
|
21
|
+
if dns_did = resolve_handle_by_dns(domain)
|
|
22
|
+
DID.new(dns_did, :dns)
|
|
23
|
+
elsif http_did = resolve_handle_by_well_known(domain)
|
|
24
|
+
DID.new(http_did, :http)
|
|
25
|
+
else
|
|
26
|
+
nil
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def resolve_handle_by_dns(domain)
|
|
31
|
+
dns_records = Resolv::DNS.open(resolv_options) { |d|
|
|
32
|
+
d.getresources("_atproto.#{domain}", Resolv::DNS::Resource::IN::TXT)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if record = dns_records.first
|
|
36
|
+
if string = record.strings.first
|
|
37
|
+
return parse_did_from_dns(string)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
nil
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def resolve_handle_by_well_known(domain)
|
|
45
|
+
resolve_handle_from_url("https://#{domain}/.well-known/atproto-did")
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def resolve_handle_from_url(url, redirects = 0)
|
|
49
|
+
url = URI(url) unless url.is_a?(URI)
|
|
50
|
+
|
|
51
|
+
response = Net::HTTP.start(url.host, url.port, use_ssl: true, open_timeout: 10, read_timeout: 10) do |http|
|
|
52
|
+
request = Net::HTTP::Get.new(url)
|
|
53
|
+
http.request(request)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
if response.is_a?(Net::HTTPSuccess)
|
|
57
|
+
if text = response.body
|
|
58
|
+
return parse_did_from_well_known(text)
|
|
59
|
+
end
|
|
60
|
+
elsif response.is_a?(Net::HTTPRedirection) && redirects < MAX_REDIRECTS
|
|
61
|
+
if location = response['Location']
|
|
62
|
+
target_url = location.include?('://') ? location : (url.origin + location)
|
|
63
|
+
return resolve_handle_from_url(target_url, redirects + 1)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
nil
|
|
68
|
+
rescue StandardError => e
|
|
69
|
+
nil
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def resolv_options
|
|
73
|
+
options = Resolv::DNS::Config.default_config_hash.dup
|
|
74
|
+
options[:nameserver] = nameserver if nameserver
|
|
75
|
+
options
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def parse_did_from_dns(txt)
|
|
79
|
+
txt =~ /\Adid\=(did\:\w+\:.*)\z/ ? $1 : nil
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def parse_did_from_well_known(text)
|
|
83
|
+
text = text.strip
|
|
84
|
+
text.lines.length == 1 && text =~ /\Adid\:\w+\:.*\z/ ? text : nil
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def resolve_did(did)
|
|
88
|
+
did = DID.new(did) if did.is_a?(String)
|
|
89
|
+
|
|
90
|
+
did.type == :plc ? resolve_did_plc(did) : resolve_did_web(did)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def resolve_did_plc(did)
|
|
94
|
+
url = "https://plc.directory/#{did}"
|
|
95
|
+
json = JSON.parse(URI.open(url).read)
|
|
96
|
+
Document.new(did, json)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def resolve_did_web(did)
|
|
100
|
+
url = "https://#{did.web_domain}/.well-known/did.json"
|
|
101
|
+
json = JSON.parse(URI.open(url).read)
|
|
102
|
+
Document.new(did, json)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def get_validated_handle(did_or_doc)
|
|
106
|
+
document = did_or_doc.is_a?(Document) ? did_or_doc : resolve_did(did_or_doc)
|
|
107
|
+
|
|
108
|
+
pick_valid_handle(document.did, document.handles)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def pick_valid_handle(did, handles)
|
|
112
|
+
handles.detect { |h| resolve_handle(h) == did }
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
data/lib/didkit/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: didkit
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0
|
|
4
|
+
version: 0.1.0
|
|
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-
|
|
11
|
+
date: 2024-03-12 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description:
|
|
14
14
|
email:
|
|
@@ -24,6 +24,9 @@ files:
|
|
|
24
24
|
- lib/didkit/did.rb
|
|
25
25
|
- lib/didkit/document.rb
|
|
26
26
|
- lib/didkit/errors.rb
|
|
27
|
+
- lib/didkit/plc_importer.rb
|
|
28
|
+
- lib/didkit/plc_operation.rb
|
|
29
|
+
- lib/didkit/resolver.rb
|
|
27
30
|
- lib/didkit/version.rb
|
|
28
31
|
- sig/didkit.rbs
|
|
29
32
|
homepage: https://github.com/mackuba/didkit
|