oydid 0.8.1 → 0.9.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/VERSION +1 -1
- data/lib/oydid.rb +95 -17
- data/spec/output/main/sample0_read.doc +1 -1
- data/spec/oydid_spec.rb +135 -0
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d3e561caa744ec1b341317d142b27f5b9d7d9dddf92b2d18aaf2117fff05cb9a
|
|
4
|
+
data.tar.gz: 6aa242687b1765148f79e9b6911d22e81339159f2f0c45445c0be1ce804fe0ad
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 35d735d7f16edf812645b91034b5c8824437144fb86b2dba19e2b165a1912279fc3e6b09916cde37004402bb309f5faad7248105b41de07f293892f7e6963d09
|
|
7
|
+
data.tar.gz: 0e333ede2a2b46e9c6264264f638cf3b54823cab9663a11af3bf776f796965210abb39b3c37555d0824f67d9b55506d3065803b1a94182097acdf0b11b9c670e
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.9.0
|
data/lib/oydid.rb
CHANGED
|
@@ -68,8 +68,15 @@ class Oydid
|
|
|
68
68
|
end
|
|
69
69
|
|
|
70
70
|
# setup
|
|
71
|
+
#
|
|
72
|
+
# did_requested is the identifier the caller asked for. dag_update
|
|
73
|
+
# overwrites "did" with every version it walks through, so by the time a
|
|
74
|
+
# DID document is rendered the requested identifier would be lost - and
|
|
75
|
+
# after an update that is exactly the one a relying party holds, e.g.
|
|
76
|
+
# printed on a product's data carrier.
|
|
71
77
|
currentDID = {
|
|
72
78
|
"did": did,
|
|
79
|
+
"did_requested": did,
|
|
73
80
|
"doc": "",
|
|
74
81
|
"log": [],
|
|
75
82
|
"doc_log_id": nil,
|
|
@@ -1058,6 +1065,18 @@ class Oydid
|
|
|
1058
1065
|
return [nil, did_info["message"].to_s]
|
|
1059
1066
|
end
|
|
1060
1067
|
|
|
1068
|
+
# A client that holds no private revocation key - a secure element does
|
|
1069
|
+
# not export one - submits the record it kept from creating or updating
|
|
1070
|
+
# the DID. Rebuilding it here is impossible, and re-signing it would not
|
|
1071
|
+
# help: the hash committed in the TERMINATE entry would no longer match.
|
|
1072
|
+
if !options[:log_revoke].nil?
|
|
1073
|
+
verified, vmsg = verify_revocation_log(did_info, options[:log_revoke], "log_revoke", options)
|
|
1074
|
+
if verified.nil?
|
|
1075
|
+
return [nil, vmsg]
|
|
1076
|
+
end
|
|
1077
|
+
return [link_revocation_log(verified, did_info), ""]
|
|
1078
|
+
end
|
|
1079
|
+
|
|
1061
1080
|
did = did_info["did"]
|
|
1062
1081
|
did_hash = did.delete_prefix("did:oyd:")
|
|
1063
1082
|
did10 = did_hash[0,10]
|
|
@@ -1224,6 +1243,15 @@ class Oydid
|
|
|
1224
1243
|
# the location extracted from the DID may be percent-encoded
|
|
1225
1244
|
# (e.g. "http%3A%2F%2Flocalhost%3A3000"); decode before using as URL
|
|
1226
1245
|
doc_location = doc_location.to_s.gsub("%3A", ":").gsub("%2F", "/")
|
|
1246
|
+
# percent_encode strips the default scheme, so a DID published to an
|
|
1247
|
+
# https location carries a bare host ("oydid.ownyourdata.eu"). Without
|
|
1248
|
+
# putting the scheme back this is not recognised as a remote location
|
|
1249
|
+
# and the revocation is written to a local file instead - which is how
|
|
1250
|
+
# the branch below used to be reached at all.
|
|
1251
|
+
if !(doc_location == "" || doc_location == "local") &&
|
|
1252
|
+
!doc_location.start_with?("http")
|
|
1253
|
+
doc_location = "https://" + doc_location
|
|
1254
|
+
end
|
|
1227
1255
|
|
|
1228
1256
|
# publish revocation log based on location
|
|
1229
1257
|
case doc_location.to_s
|
|
@@ -1236,10 +1264,10 @@ class Oydid
|
|
|
1236
1264
|
return [nil, msg]
|
|
1237
1265
|
end
|
|
1238
1266
|
else
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1267
|
+
# did_old / did10_old were never defined here - they are locals of
|
|
1268
|
+
# generate_base. Writing the revocation next to the previous DID is
|
|
1269
|
+
# the job of the caller that knows about it.
|
|
1270
|
+
write_private_storage(revoc_log.to_json, did10 + ".log")
|
|
1243
1271
|
end
|
|
1244
1272
|
|
|
1245
1273
|
return [did, ""]
|
|
@@ -1405,6 +1433,61 @@ class Oydid
|
|
|
1405
1433
|
wd
|
|
1406
1434
|
end
|
|
1407
1435
|
|
|
1436
|
+
# Identifiers of every published version of a DID, oldest first.
|
|
1437
|
+
#
|
|
1438
|
+
# A did:oyd is the hash over its own DID document, so every update mints a
|
|
1439
|
+
# new identifier while the earlier ones stay resolvable through the log.
|
|
1440
|
+
# The version reached by traversing the log is the canonical identifier,
|
|
1441
|
+
# every other version identifier is logically equivalent to it - which is
|
|
1442
|
+
# what DID Core expresses as didDocumentMetadata canonicalId/equivalentId.
|
|
1443
|
+
#
|
|
1444
|
+
# This lives in the gem rather than in the resolving controllers because
|
|
1445
|
+
# there are three of them (repository resolve and resolve_full, plus the
|
|
1446
|
+
# uniresolver plugin) and they had drifted into computing this list with
|
|
1447
|
+
# different rules.
|
|
1448
|
+
#
|
|
1449
|
+
# Returns [canonicalId, equivalentIds]; equivalentIds never contains the
|
|
1450
|
+
# identifier the DID document itself carries as `id` (see document_id).
|
|
1451
|
+
def self.version_ids(did_info)
|
|
1452
|
+
canonical = percent_encode(did_info["did"].to_s)
|
|
1453
|
+
if !canonical.start_with?("did:oyd:")
|
|
1454
|
+
canonical = "did:oyd:" + canonical
|
|
1455
|
+
end
|
|
1456
|
+
own = document_id(did_info)
|
|
1457
|
+
equivalentIds = []
|
|
1458
|
+
did_info["log"].each do |log|
|
|
1459
|
+
if log["op"] == 2 || log["op"] == 3
|
|
1460
|
+
eid = percent_encode("did:oyd:" + log["doc"].to_s)
|
|
1461
|
+
if eid != own
|
|
1462
|
+
equivalentIds << eid
|
|
1463
|
+
end
|
|
1464
|
+
end
|
|
1465
|
+
end unless did_info["log"].nil?
|
|
1466
|
+
[canonical, equivalentIds]
|
|
1467
|
+
end
|
|
1468
|
+
|
|
1469
|
+
# The identifier a resolved DID document carries as `id`.
|
|
1470
|
+
#
|
|
1471
|
+
# A did:oyd is the hash over its own document, so an update mints a new one
|
|
1472
|
+
# and the resolution walks the log to the newest version. Answering with that
|
|
1473
|
+
# newest identifier means a relying party never sees the DID it asked for -
|
|
1474
|
+
# the one on the data carrier, in the credential, in the database. So the
|
|
1475
|
+
# requested identifier is what goes into the document; which version it is
|
|
1476
|
+
# remains readable from didDocumentMetadata canonicalId.
|
|
1477
|
+
#
|
|
1478
|
+
# Callers that build a did_info by hand - the registrar endpoints do, for a
|
|
1479
|
+
# DID that was just created or updated - carry no did_requested and keep
|
|
1480
|
+
# getting the identifier they passed in.
|
|
1481
|
+
def self.document_id(did_info)
|
|
1482
|
+
did = did_info["did_requested"].to_s
|
|
1483
|
+
did = did_info["did"].to_s if did == ""
|
|
1484
|
+
did = percent_encode(did)
|
|
1485
|
+
if !did.start_with?("did:oyd:")
|
|
1486
|
+
did = "did:oyd:" + did
|
|
1487
|
+
end
|
|
1488
|
+
did
|
|
1489
|
+
end
|
|
1490
|
+
|
|
1408
1491
|
def self.w3c(did_info, options)
|
|
1409
1492
|
# check if doc is already W3C DID
|
|
1410
1493
|
is_already_w3c_did = (did_info.transform_keys(&:to_s)["doc"]["doc"].has_key?("@context") &&
|
|
@@ -1413,10 +1496,7 @@ class Oydid
|
|
|
1413
1496
|
if is_already_w3c_did
|
|
1414
1497
|
return did_info.transform_keys(&:to_s)["doc"]["doc"]
|
|
1415
1498
|
end
|
|
1416
|
-
did =
|
|
1417
|
-
if !did.start_with?("did:oyd:")
|
|
1418
|
-
did = "did:oyd:" + did
|
|
1419
|
-
end
|
|
1499
|
+
did = document_id(did_info)
|
|
1420
1500
|
|
|
1421
1501
|
didDoc = did_info.dup.transform_keys(&:to_s)["doc"]
|
|
1422
1502
|
pubDocKey = didDoc["key"].split(":")[0] rescue ""
|
|
@@ -1547,15 +1627,13 @@ class Oydid
|
|
|
1547
1627
|
end
|
|
1548
1628
|
end
|
|
1549
1629
|
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
end
|
|
1558
|
-
end unless did_info["log"].nil?
|
|
1630
|
+
# alsoKnownAs is a statement about the DID *subject*, and DID Core calls
|
|
1631
|
+
# it best practice not to read it as equivalence unless the relationship
|
|
1632
|
+
# is reciprocated - which it cannot be here, because all versions resolve
|
|
1633
|
+
# to the same document. It stays for backwards compatibility; the
|
|
1634
|
+
# authoritative statement is didDocumentMetadata canonicalId/equivalentId,
|
|
1635
|
+
# built from the same list.
|
|
1636
|
+
equivalentIds = version_ids(did_info).last
|
|
1559
1637
|
if equivalentIds.length > 0
|
|
1560
1638
|
wd["alsoKnownAs"] = equivalentIds
|
|
1561
1639
|
end
|
|
@@ -1 +1 @@
|
|
|
1
|
-
[{"did":"zQmaBZTghndXTgxNwfbdpVLWdFf6faYE4oeuN2zzXdQt1kh","doc":{"doc":{"simple":"example"},"key":"z6MusYB5iT5krCHYsZ76EzBaTdRwGKsaBhMcSbrXaPJgkuRQ:z6Mv7EYihbAat6Wq7GsjNsjcxt58dZT8fmsRjQGTkYamYrjB","log":"zQmVwMvovLy5KNYHHVHQ1wv8J7y9L6UPE8eyU4tzypFWtYe"},"log":[{"ts":1641224736,"op":2,"doc":"zQmaBZTghndXTgxNwfbdpVLWdFf6faYE4oeuN2zzXdQt1kh","sig":"z3Kb5qeReCqr3ftxpf2i5UypUwrzrVkyspMtaDcb6e9YdHVSptcAFgvwbgk3qWqspTcGiKDYKXZZh8g6XyM2WPmNp","previous":[]},{"ts":1641224736,"op":0,"doc":"zQmT8SG7a238bF7wdV7LdrEAQpimqhKGor7CQsjtCYdZdTS","sig":"z63hu8LseptBrvB2kEDwhPP35sBj7JDDJsEDW85cjRkrjjac9ZV3HxPW9NVKewHcQYwrVLVsnDCcm1RjbEARE5rJU","previous":[]}],"doc_log_id":0,"termination_log_id":1,"error":0,"message":"","verification":"identifier: zQmaBZTghndXTgxNwfbdpVLWdFf6faYE4oeuN2zzXdQt1kh\n✅ is hash of DID Document:\n{\n \"doc\": {\n \"simple\": \"example\"\n },\n \"key\": \"z6MusYB5iT5krCHYsZ76EzBaTdRwGKsaBhMcSbrXaPJgkuRQ:z6Mv7EYihbAat6Wq7GsjNsjcxt58dZT8fmsRjQGTkYamYrjB\",\n \"log\": \"zQmVwMvovLy5KNYHHVHQ1wv8J7y9L6UPE8eyU4tzypFWtYe\"\n}\n(Details: https://ownyourdata.github.io/oydid/#calculate_hash)\n\n'log' reference in DID Document: zQmVwMvovLy5KNYHHVHQ1wv8J7y9L6UPE8eyU4tzypFWtYe\n✅ is hash of TERMINATE log record:\n{\n \"ts\": 1641224736,\n \"op\": 0,\n \"doc\": \"zQmT8SG7a238bF7wdV7LdrEAQpimqhKGor7CQsjtCYdZdTS\",\n \"sig\": \"z63hu8LseptBrvB2kEDwhPP35sBj7JDDJsEDW85cjRkrjjac9ZV3HxPW9NVKewHcQYwrVLVsnDCcm1RjbEARE5rJU\",\n \"previous\": []\n}\n(Details: https://ownyourdata.github.io/oydid/#calculate_hash)\n\nRevocation reference in log record: zQmT8SG7a238bF7wdV7LdrEAQpimqhKGor7CQsjtCYdZdTS\n✅ cannot find revocation record searching at\n- https://oydid.ownyourdata.eu\n(Details: https://ownyourdata.github.io/oydid/#retrieve_log)\n\n","full_log":[{"ts":1641224736,"op":2,"doc":"zQmaBZTghndXTgxNwfbdpVLWdFf6faYE4oeuN2zzXdQt1kh","sig":"z3Kb5qeReCqr3ftxpf2i5UypUwrzrVkyspMtaDcb6e9YdHVSptcAFgvwbgk3qWqspTcGiKDYKXZZh8g6XyM2WPmNp","previous":[]},{"ts":1641224736,"op":0,"doc":"zQmT8SG7a238bF7wdV7LdrEAQpimqhKGor7CQsjtCYdZdTS","sig":"z63hu8LseptBrvB2kEDwhPP35sBj7JDDJsEDW85cjRkrjjac9ZV3HxPW9NVKewHcQYwrVLVsnDCcm1RjbEARE5rJU","previous":[]}]},""]
|
|
1
|
+
[{"did":"zQmaBZTghndXTgxNwfbdpVLWdFf6faYE4oeuN2zzXdQt1kh","did_requested":"did:oyd:zQmaBZTghndXTgxNwfbdpVLWdFf6faYE4oeuN2zzXdQt1kh","doc":{"doc":{"simple":"example"},"key":"z6MusYB5iT5krCHYsZ76EzBaTdRwGKsaBhMcSbrXaPJgkuRQ:z6Mv7EYihbAat6Wq7GsjNsjcxt58dZT8fmsRjQGTkYamYrjB","log":"zQmVwMvovLy5KNYHHVHQ1wv8J7y9L6UPE8eyU4tzypFWtYe"},"log":[{"ts":1641224736,"op":2,"doc":"zQmaBZTghndXTgxNwfbdpVLWdFf6faYE4oeuN2zzXdQt1kh","sig":"z3Kb5qeReCqr3ftxpf2i5UypUwrzrVkyspMtaDcb6e9YdHVSptcAFgvwbgk3qWqspTcGiKDYKXZZh8g6XyM2WPmNp","previous":[]},{"ts":1641224736,"op":0,"doc":"zQmT8SG7a238bF7wdV7LdrEAQpimqhKGor7CQsjtCYdZdTS","sig":"z63hu8LseptBrvB2kEDwhPP35sBj7JDDJsEDW85cjRkrjjac9ZV3HxPW9NVKewHcQYwrVLVsnDCcm1RjbEARE5rJU","previous":[]}],"doc_log_id":0,"termination_log_id":1,"error":0,"message":"","verification":"identifier: zQmaBZTghndXTgxNwfbdpVLWdFf6faYE4oeuN2zzXdQt1kh\n✅ is hash of DID Document:\n{\n \"doc\": {\n \"simple\": \"example\"\n },\n \"key\": \"z6MusYB5iT5krCHYsZ76EzBaTdRwGKsaBhMcSbrXaPJgkuRQ:z6Mv7EYihbAat6Wq7GsjNsjcxt58dZT8fmsRjQGTkYamYrjB\",\n \"log\": \"zQmVwMvovLy5KNYHHVHQ1wv8J7y9L6UPE8eyU4tzypFWtYe\"\n}\n(Details: https://ownyourdata.github.io/oydid/#calculate_hash)\n\n'log' reference in DID Document: zQmVwMvovLy5KNYHHVHQ1wv8J7y9L6UPE8eyU4tzypFWtYe\n✅ is hash of TERMINATE log record:\n{\n \"ts\": 1641224736,\n \"op\": 0,\n \"doc\": \"zQmT8SG7a238bF7wdV7LdrEAQpimqhKGor7CQsjtCYdZdTS\",\n \"sig\": \"z63hu8LseptBrvB2kEDwhPP35sBj7JDDJsEDW85cjRkrjjac9ZV3HxPW9NVKewHcQYwrVLVsnDCcm1RjbEARE5rJU\",\n \"previous\": []\n}\n(Details: https://ownyourdata.github.io/oydid/#calculate_hash)\n\nRevocation reference in log record: zQmT8SG7a238bF7wdV7LdrEAQpimqhKGor7CQsjtCYdZdTS\n✅ cannot find revocation record searching at\n- https://oydid.ownyourdata.eu\n(Details: https://ownyourdata.github.io/oydid/#retrieve_log)\n\n","full_log":[{"ts":1641224736,"op":2,"doc":"zQmaBZTghndXTgxNwfbdpVLWdFf6faYE4oeuN2zzXdQt1kh","sig":"z3Kb5qeReCqr3ftxpf2i5UypUwrzrVkyspMtaDcb6e9YdHVSptcAFgvwbgk3qWqspTcGiKDYKXZZh8g6XyM2WPmNp","previous":[]},{"ts":1641224736,"op":0,"doc":"zQmT8SG7a238bF7wdV7LdrEAQpimqhKGor7CQsjtCYdZdTS","sig":"z63hu8LseptBrvB2kEDwhPP35sBj7JDDJsEDW85cjRkrjjac9ZV3HxPW9NVKewHcQYwrVLVsnDCcm1RjbEARE5rJU","previous":[]}]},""]
|
data/spec/oydid_spec.rb
CHANGED
|
@@ -584,4 +584,139 @@ describe "OYDID handling" do
|
|
|
584
584
|
end
|
|
585
585
|
end
|
|
586
586
|
|
|
587
|
+
# Identifiers of the published versions of a DID. Every update mints a new
|
|
588
|
+
# one, so a resolver has to be able to say which is current and which are
|
|
589
|
+
# earlier - didDocumentMetadata canonicalId and equivalentId.
|
|
590
|
+
describe "version_ids" do
|
|
591
|
+
let(:first_did) { "zQmSE1hzumtZ7AoK1qhHf4t5kiKsujMsJSHqoXtWrdd7K7W" }
|
|
592
|
+
let(:second_did) { "zQmfEb3KgYZjZUPLTHPmFPdcV6peF5itB5NmJ9N6gaxxE8K" }
|
|
593
|
+
|
|
594
|
+
it "reports a freshly created DID as canonical without equivalents" do
|
|
595
|
+
canonical, equivalent = Oydid.version_ids(
|
|
596
|
+
"did" => "did:oyd:" + first_did,
|
|
597
|
+
"log" => [{ "op" => 2, "doc" => first_did },
|
|
598
|
+
{ "op" => 0, "doc" => "terminate" }])
|
|
599
|
+
expect(canonical).to eq "did:oyd:" + first_did
|
|
600
|
+
expect(equivalent).to eq []
|
|
601
|
+
end
|
|
602
|
+
|
|
603
|
+
it "names the current version canonical and the previous one equivalent" do
|
|
604
|
+
canonical, equivalent = Oydid.version_ids(
|
|
605
|
+
"did" => "did:oyd:" + second_did,
|
|
606
|
+
"log" => [{ "op" => 1, "doc" => "revoke" },
|
|
607
|
+
{ "op" => 2, "doc" => first_did },
|
|
608
|
+
{ "op" => 3, "doc" => second_did },
|
|
609
|
+
{ "op" => 0, "doc" => "terminate" }])
|
|
610
|
+
expect(canonical).to eq "did:oyd:" + second_did
|
|
611
|
+
expect(equivalent).to eq ["did:oyd:" + first_did]
|
|
612
|
+
end
|
|
613
|
+
|
|
614
|
+
it "keeps every earlier version, oldest first" do
|
|
615
|
+
canonical, equivalent = Oydid.version_ids(
|
|
616
|
+
"did" => "did:oyd:zC",
|
|
617
|
+
"log" => [{ "op" => 2, "doc" => "zA" },
|
|
618
|
+
{ "op" => 3, "doc" => "zB" },
|
|
619
|
+
{ "op" => 3, "doc" => "zC" }])
|
|
620
|
+
expect(canonical).to eq "did:oyd:zC"
|
|
621
|
+
expect(equivalent).to eq ["did:oyd:zA", "did:oyd:zB"]
|
|
622
|
+
end
|
|
623
|
+
|
|
624
|
+
it "adds the method prefix when the resolved DID carries none" do
|
|
625
|
+
canonical, = Oydid.version_ids("did" => second_did, "log" => [])
|
|
626
|
+
expect(canonical).to eq "did:oyd:" + second_did
|
|
627
|
+
end
|
|
628
|
+
|
|
629
|
+
it "percent-encodes a location suffix" do
|
|
630
|
+
canonical, equivalent = Oydid.version_ids(
|
|
631
|
+
"did" => "did:oyd:" + second_did + "@https://example.org",
|
|
632
|
+
"log" => [{ "op" => 2, "doc" => first_did + "@https://example.org" },
|
|
633
|
+
{ "op" => 3, "doc" => second_did + "@https://example.org" }])
|
|
634
|
+
expect(canonical).to eq "did:oyd:" + second_did + "%40example.org"
|
|
635
|
+
expect(equivalent).to eq ["did:oyd:" + first_did + "%40example.org"]
|
|
636
|
+
end
|
|
637
|
+
|
|
638
|
+
it "tolerates a missing log" do
|
|
639
|
+
canonical, equivalent = Oydid.version_ids("did" => "did:oyd:" + second_did)
|
|
640
|
+
expect(canonical).to eq "did:oyd:" + second_did
|
|
641
|
+
expect(equivalent).to eq []
|
|
642
|
+
end
|
|
643
|
+
|
|
644
|
+
# w3c builds alsoKnownAs from the same list. The two used to be computed
|
|
645
|
+
# separately in three places and drifted apart; this guards the merge.
|
|
646
|
+
#
|
|
647
|
+
# The canonical DID has to be one this suite stubs: w3c looks up delegation
|
|
648
|
+
# keys for it, and WebMock::NetConnectNotAllowedError descends from Exception
|
|
649
|
+
# rather than StandardError, so the inline `rescue []` around
|
|
650
|
+
# getDelegatedPubKeysFromDID does not catch it. Only the canonical DID is
|
|
651
|
+
# fetched - log entries are read, not resolved.
|
|
652
|
+
it "agrees with the alsoKnownAs the DID document carries" do
|
|
653
|
+
stubbed_did = "zQmaBZTghndXTgxNwfbdpVLWdFf6faYE4oeuN2zzXdQt1kh"
|
|
654
|
+
did_info = {
|
|
655
|
+
"did" => "did:oyd:" + stubbed_did,
|
|
656
|
+
"doc" => { "doc" => { "hello" => "world" },
|
|
657
|
+
"key" => "z6MktULudTtAsAhRegYPiZ6631RV3viv12qd4GQF8z1xB22S:" \
|
|
658
|
+
"z6MkqGC3nWZhYieEVTVDKW5v588CiGfsDSmRVG9ZwwWTvLSK" },
|
|
659
|
+
"log" => [{ "op" => 2, "doc" => first_did },
|
|
660
|
+
{ "op" => 3, "doc" => stubbed_did }]
|
|
661
|
+
}
|
|
662
|
+
wd = Oydid.w3c(Marshal.load(Marshal.dump(did_info)), {})
|
|
663
|
+
expect(wd["alsoKnownAs"]).to eq ["did:oyd:" + first_did]
|
|
664
|
+
expect(wd["alsoKnownAs"]).to eq Oydid.version_ids(did_info).last
|
|
665
|
+
end
|
|
666
|
+
end
|
|
667
|
+
|
|
668
|
+
# Which identifier a resolved DID document announces as its own. An update
|
|
669
|
+
# mints a new did:oyd, but the identifier a relying party holds is the one it
|
|
670
|
+
# asked for - that is the one the document has to carry.
|
|
671
|
+
describe "document_id" do
|
|
672
|
+
let(:first_did) { "zQmSE1hzumtZ7AoK1qhHf4t5kiKsujMsJSHqoXtWrdd7K7W" }
|
|
673
|
+
let(:second_did) { "zQmfEb3KgYZjZUPLTHPmFPdcV6peF5itB5NmJ9N6gaxxE8K" }
|
|
674
|
+
# a DID this suite stubs - w3c looks up delegation keys for whatever it
|
|
675
|
+
# emits as id, see the note on the alsoKnownAs example above
|
|
676
|
+
let(:stubbed_did) { "zQmaBZTghndXTgxNwfbdpVLWdFf6faYE4oeuN2zzXdQt1kh" }
|
|
677
|
+
|
|
678
|
+
it "answers with the requested DID when the resolver kept it" do
|
|
679
|
+
expect(Oydid.document_id("did" => "did:oyd:" + second_did,
|
|
680
|
+
"did_requested" => "did:oyd:" + first_did))
|
|
681
|
+
.to eq "did:oyd:" + first_did
|
|
682
|
+
end
|
|
683
|
+
|
|
684
|
+
it "falls back to the resolved DID for a did_info built by hand" do
|
|
685
|
+
expect(Oydid.document_id("did" => "did:oyd:" + second_did))
|
|
686
|
+
.to eq "did:oyd:" + second_did
|
|
687
|
+
end
|
|
688
|
+
|
|
689
|
+
it "adds the method prefix and percent-encodes a location suffix" do
|
|
690
|
+
expect(Oydid.document_id("did" => "", "did_requested" => first_did + "@https://example.org"))
|
|
691
|
+
.to eq "did:oyd:" + first_did + "%40example.org"
|
|
692
|
+
end
|
|
693
|
+
|
|
694
|
+
# the point of the exercise: a DID printed on a data carrier keeps naming
|
|
695
|
+
# itself after an update, while the current version stays readable
|
|
696
|
+
it "keeps the requested DID as id and moves the current one to alsoKnownAs" do
|
|
697
|
+
did_info = {
|
|
698
|
+
"did" => "did:oyd:" + second_did,
|
|
699
|
+
"did_requested" => "did:oyd:" + stubbed_did,
|
|
700
|
+
"doc" => { "doc" => { "hello" => "world" },
|
|
701
|
+
"key" => "z6MktULudTtAsAhRegYPiZ6631RV3viv12qd4GQF8z1xB22S:" \
|
|
702
|
+
"z6MkqGC3nWZhYieEVTVDKW5v588CiGfsDSmRVG9ZwwWTvLSK" },
|
|
703
|
+
"log" => [{ "op" => 2, "doc" => stubbed_did },
|
|
704
|
+
{ "op" => 3, "doc" => second_did }]
|
|
705
|
+
}
|
|
706
|
+
wd = Oydid.w3c(Marshal.load(Marshal.dump(did_info)), {})
|
|
707
|
+
expect(wd["id"]).to eq "did:oyd:" + stubbed_did
|
|
708
|
+
expect(wd["alsoKnownAs"]).to eq ["did:oyd:" + second_did]
|
|
709
|
+
# the entries of verificationMethod are built as hash literals with
|
|
710
|
+
# "id": - Ruby makes that a symbol key, the surrounding keys are strings
|
|
711
|
+
expect(wd["verificationMethod"].first[:id]).to start_with("did:oyd:" + stubbed_did + "#")
|
|
712
|
+
expect(wd["verificationMethod"].first[:controller]).to eq "did:oyd:" + stubbed_did
|
|
713
|
+
|
|
714
|
+
# canonicalId still names the version the log resolves to, and the
|
|
715
|
+
# equivalent list is everything the document does not call itself
|
|
716
|
+
canonical, equivalent = Oydid.version_ids(did_info)
|
|
717
|
+
expect(canonical).to eq "did:oyd:" + second_did
|
|
718
|
+
expect(equivalent).to eq ["did:oyd:" + second_did]
|
|
719
|
+
end
|
|
720
|
+
end
|
|
721
|
+
|
|
587
722
|
end
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: oydid
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.9.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Christoph Fabianek
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-08-
|
|
10
|
+
date: 2026-08-23 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: simple_dag
|