Skip to content
This repository has been archived by the owner on Mar 27, 2024. It is now read-only.

Commit

Permalink
feat: add did document support for alsoKnownAs
Browse files Browse the repository at this point in the history
Signed-off-by: Chris Abernethy <brownoxford@gmail.com>
  • Loading branch information
brownoxford committed Jul 14, 2022
1 parent d5d31b7 commit 6f14536
Show file tree
Hide file tree
Showing 7 changed files with 168 additions and 15 deletions.
23 changes: 19 additions & 4 deletions pkg/doc/did/doc.go
Expand Up @@ -263,6 +263,7 @@ func ParseDocumentResolution(data []byte) (*DocResolution, error) {
type Doc struct {
Context []string
ID string
AlsoKnownAs []string
VerificationMethod []VerificationMethod
Service []Service
Authentication []Verification
Expand Down Expand Up @@ -428,6 +429,7 @@ func NewReferencedVerification(vm *VerificationMethod, r VerificationRelationshi
type rawDoc struct {
Context interface{} `json:"@context,omitempty"`
ID string `json:"id,omitempty"`
AlsoKnownAs []interface{} `json:"alsoKnownAs,omitempty"`
VerificationMethod []map[string]interface{} `json:"verificationMethod,omitempty"`
PublicKey []map[string]interface{} `json:"publicKey,omitempty"`
Service []map[string]interface{} `json:"service,omitempty"`
Expand Down Expand Up @@ -489,9 +491,10 @@ func ParseDocument(data []byte) (*Doc, error) {
}

doc := &Doc{
ID: raw.ID,
Created: raw.Created,
Updated: raw.Updated,
ID: raw.ID,
AlsoKnownAs: stringArray(raw.AlsoKnownAs),
Created: raw.Created,
Updated: raw.Updated,
}

context, baseURI := parseContext(raw.Context)
Expand Down Expand Up @@ -1114,6 +1117,8 @@ func (doc *Doc) JSONBytes() ([]byte, error) {
context = doc.Context[0]
}

aka := populateRawAlsoKnownAs(doc.AlsoKnownAs)

vm, err := populateRawVM(context, doc.ID, doc.processingMeta.baseURI, doc.VerificationMethod)
if err != nil {
return nil, fmt.Errorf("JSON unmarshalling of Verification Method failed: %w", err)
Expand Down Expand Up @@ -1148,7 +1153,7 @@ func (doc *Doc) JSONBytes() ([]byte, error) {
}

raw := &rawDoc{
Context: doc.Context, ID: doc.ID, VerificationMethod: vm,
Context: doc.Context, ID: doc.ID, AlsoKnownAs: aka, VerificationMethod: vm,
Authentication: auths, AssertionMethod: assertionMethods, CapabilityDelegation: capabilityDelegations,
CapabilityInvocation: capabilityInvocations, KeyAgreement: keyAgreements,
Service: populateRawServices(doc.Service, doc.ID, doc.processingMeta.baseURI), Created: doc.Created,
Expand Down Expand Up @@ -1395,6 +1400,16 @@ func populateRawServices(services []Service, didID, baseURI string) []map[string
return rawServices
}

func populateRawAlsoKnownAs(aka []string) []interface{} {
rawAka := make([]interface{}, len(aka))

for i, v := range aka {
rawAka[i] = v
}

return rawAka
}

func populateRawVM(context, didID, baseURI string, pks []VerificationMethod) ([]map[string]interface{}, error) {
var rawVM []map[string]interface{}

Expand Down
124 changes: 113 additions & 11 deletions pkg/doc/did/doc_test.go
Expand Up @@ -89,6 +89,10 @@ func TestValidWithDocBase(t *testing.T) {
// test doc id
require.Equal(t, doc.ID, "did:example:123456789abcdefghi")

// test alsoKnownAs
require.Equal(t, 1, len(doc.AlsoKnownAs))
require.Equal(t, "did:example:123", doc.AlsoKnownAs[0])

hexDecodeValue, err := hex.DecodeString("02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71")
block, _ := pem.Decode([]byte(pemPK))
require.NotNil(t, block)
Expand Down Expand Up @@ -175,6 +179,8 @@ func TestDocResolution(t *testing.T) {
require.Equal(t, 1, len(d.Context))
require.Equal(t, "https://w3id.org/did-resolution/v1", d.Context[0])
require.Equal(t, "did:example:21tDAKCERh95uGgKbJNHYp", d.DIDDocument.ID)
require.Equal(t, 1, len(d.DIDDocument.AlsoKnownAs))
require.Equal(t, "did:example:123", d.DIDDocument.AlsoKnownAs[0])
require.Equal(t, true, d.DocumentMetadata.Method.Published)
require.Equal(t, "did:ex:123333", d.DocumentMetadata.CanonicalID)

Expand All @@ -187,6 +193,8 @@ func TestDocResolution(t *testing.T) {
require.Equal(t, 1, len(d.Context))
require.Equal(t, "https://w3id.org/did-resolution/v1", d.Context[0])
require.Equal(t, "did:example:21tDAKCERh95uGgKbJNHYp", d.DIDDocument.ID)
require.Equal(t, 1, len(d.DIDDocument.AlsoKnownAs))
require.Equal(t, "did:example:123", d.DIDDocument.AlsoKnownAs[0])
require.Equal(t, true, d.DocumentMetadata.Method.Published)
require.Equal(t, "did:ex:123333", d.DocumentMetadata.CanonicalID)
})
Expand All @@ -209,6 +217,10 @@ func TestValid(t *testing.T) {
// test doc id
require.Equal(t, doc.ID, "did:example:21tDAKCERh95uGgKbJNHYp")

// test alsoKnownAs
require.Equal(t, 1, len(doc.AlsoKnownAs))
require.Equal(t, "did:example:123", doc.AlsoKnownAs[0])

hexDecodeValue, err := hex.DecodeString("02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71")
block, _ := pem.Decode([]byte(pemPK))
require.NotNil(t, block)
Expand Down Expand Up @@ -561,6 +573,96 @@ func TestValidateDidDocID(t *testing.T) {
})
}

func TestValidateDidDocAlsoKnownAs(t *testing.T) {
t.Run("test did doc with duplicate alsoKnownAs entries", func(t *testing.T) {
docs := []string{validDoc, validDocV011}
for _, d := range docs {
raw := &rawDoc{}
require.NoError(t, json.Unmarshal([]byte(d), &raw))
raw.AlsoKnownAs = []interface{}{"did:example:123", "did:example:123"}
bytes, err := json.Marshal(raw)
require.NoError(t, err)
err = validate(bytes, raw.schemaLoader())
require.Error(t, err)
require.Contains(t, err.Error(), "must be unique")
}
})

t.Run("test did doc with non-uri alsoKnownAs string", func(t *testing.T) {
docs := []string{validDoc, validDocV011}
for _, d := range docs {
raw := &rawDoc{}
require.NoError(t, json.Unmarshal([]byte(d), &raw))
raw.AlsoKnownAs = []interface{}{"not.a.valid.uri"}
bytes, err := json.Marshal(raw)
require.NoError(t, err)
err = validate(bytes, raw.schemaLoader())
require.Error(t, err)
require.Contains(t, err.Error(), "Does not match format 'uri'")
}
})

t.Run("test did doc with non-string alsoKnownAs entry", func(t *testing.T) {
docs := []string{validDoc, validDocV011}
for _, d := range docs {
raw := &rawDoc{}
require.NoError(t, json.Unmarshal([]byte(d), &raw))
raw.AlsoKnownAs = []interface{}{0}
bytes, err := json.Marshal(raw)
require.NoError(t, err)
err = validate(bytes, raw.schemaLoader())
require.Error(t, err)
require.Contains(t, err.Error(), "Invalid type. Expected: string")
}
})

t.Run("test did doc with empty alsoKnownAs", func(t *testing.T) {
docs := []string{validDoc, validDocV011}
for _, d := range docs {
raw := &rawDoc{}
require.NoError(t, json.Unmarshal([]byte(d), &raw))
raw.AlsoKnownAs = nil
bytes, err := json.Marshal(raw)
require.NoError(t, err)
err = validate(bytes, raw.schemaLoader())
require.NoError(t, err)
}
})

t.Run("test did doc with zero-length alsoKnownAs", func(t *testing.T) {
docs := []string{validDoc, validDocV011}
for _, d := range docs {
raw := &rawDoc{}
require.NoError(t, json.Unmarshal([]byte(d), &raw))
raw.AlsoKnownAs = []interface{}{}
bytes, err := json.Marshal(raw)
require.NoError(t, err)
err = validate(bytes, raw.schemaLoader())
require.NoError(t, err)
}
})

t.Run("test did doc with valid alsoKnownAs entries", func(t *testing.T) {
docs := []string{validDoc, validDocV011}
valid := []interface{}{
"did:example:123",
"https://social.example/username",
"urn:uuid:1231",
}
for _, d := range docs {
for _, aka := range valid {
raw := &rawDoc{}
require.NoError(t, json.Unmarshal([]byte(d), &raw))
raw.AlsoKnownAs = []interface{}{aka}
bytes, err := json.Marshal(raw)
require.NoError(t, err)
err = validate(bytes, raw.schemaLoader())
require.NoError(t, err)
}
}
})
}

func TestValidateDidDocPublicKey(t *testing.T) {
t.Run("test did doc with empty public key", func(t *testing.T) {
docs := []string{validDoc, validDocV011}
Expand Down Expand Up @@ -1498,20 +1600,20 @@ func TestDIDSchemas(t *testing.T) {
didStr: `{
"@context": "https://w3id.org/did/v0.11",
"id": "did:w123:world",
"assertionMethod": ["did:w123:world#z6MksHh7qHWvybLg5QTPPdG2DgEjjduBDArV9EF9mRiRzMBN",
"did:w123:world#_Qq0UL2Fq651Q0Fjd6TvnYE-faHiOpRlPVQcY_-tA4A",
"did:w123:world#_TKzHv2jFIyvdTGF1Dsgwngfdg3SH6TpDv0Ta1aOEkw",
"assertionMethod": ["did:w123:world#z6MksHh7qHWvybLg5QTPPdG2DgEjjduBDArV9EF9mRiRzMBN",
"did:w123:world#_Qq0UL2Fq651Q0Fjd6TvnYE-faHiOpRlPVQcY_-tA4A",
"did:w123:world#_TKzHv2jFIyvdTGF1Dsgwngfdg3SH6TpDv0Ta1aOEkw",
"did:w123:world#NjQ6Y_ZMj6IUK_XkgCDwtKHlNTUTVjEYOWZtxhp1n-E"],
"authentication": ["did:w123:world#z6MksHh7qHWvybLg5QTPPdG2DgEjjduBDArV9EF9mRiRzMBN", "",
"did:w123:world#_TKzHv2jFIyvdTGF1Dsgwngfdg3SH6TpDv0Ta1aOEkw",
"authentication": ["did:w123:world#z6MksHh7qHWvybLg5QTPPdG2DgEjjduBDArV9EF9mRiRzMBN", "",
"did:w123:world#_TKzHv2jFIyvdTGF1Dsgwngfdg3SH6TpDv0Ta1aOEkw",
"did:w123:world#NjQ6Y_ZMj6IUK_XkgCDwtKHlNTUTVjEYOWZtxhp1n-E"],
"capabilityDelegation": ["did:w123:world#z6MksHh7qHWvybLg5QTPPdG2DgEjjduBDArV9EF9mRiRzMBN",
"did:w123:world#_Qq0UL2Fq651Q0Fjd6TvnYE-faHiOpRlPVQcY_-tA4A",
"did:w123:world#_TKzHv2jFIyvdTGF1Dsgwngfdg3SH6TpDv0Ta1aOEkw",
"capabilityDelegation": ["did:w123:world#z6MksHh7qHWvybLg5QTPPdG2DgEjjduBDArV9EF9mRiRzMBN",
"did:w123:world#_Qq0UL2Fq651Q0Fjd6TvnYE-faHiOpRlPVQcY_-tA4A",
"did:w123:world#_TKzHv2jFIyvdTGF1Dsgwngfdg3SH6TpDv0Ta1aOEkw",
"did:w123:world#NjQ6Y_ZMj6IUK_XkgCDwtKHlNTUTVjEYOWZtxhp1n-E"],
"capabilityInvocation": ["did:w123:world#z6MksHh7qHWvybLg5QTPPdG2DgEjjduBDArV9EF9mRiRzMBN",
"did:w123:world#_Qq0UL2Fq651Q0Fjd6TvnYE-faHiOpRlPVQcY_-tA4A",
"did:w123:world#_TKzHv2jFIyvdTGF1Dsgwngfdg3SH6TpDv0Ta1aOEkw",
"capabilityInvocation": ["did:w123:world#z6MksHh7qHWvybLg5QTPPdG2DgEjjduBDArV9EF9mRiRzMBN",
"did:w123:world#_Qq0UL2Fq651Q0Fjd6TvnYE-faHiOpRlPVQcY_-tA4A",
"did:w123:world#_TKzHv2jFIyvdTGF1Dsgwngfdg3SH6TpDv0Ta1aOEkw",
"did:w123:world#NjQ6Y_ZMj6IUK_XkgCDwtKHlNTUTVjEYOWZtxhp1n-E"],
"keyAgreement": [{
"id": "did:w123:world#zC5iai1sL93gQxn8LKh1i42fTbpfar65dVx4NYznYfG3Y5",
Expand Down
24 changes: 24 additions & 0 deletions pkg/doc/did/schema.go
Expand Up @@ -44,6 +44,14 @@ const (
"id": {
"type": "string"
},
"alsoKnownAs": {
"type": "array",
"items": {
"type": "string",
"format": "uri"
},
"uniqueItems": true
},
"publicKey": {
"type": "array",
"items": {
Expand Down Expand Up @@ -219,6 +227,14 @@ const (
"id": {
"type": "string"
},
"alsoKnownAs": {
"type": "array",
"items": {
"type": "string",
"format": "uri"
},
"uniqueItems": true
},
"publicKey": {
"type": "array",
"items": {
Expand Down Expand Up @@ -406,6 +422,14 @@ const (
"id": {
"type": "string"
},
"alsoKnownAs": {
"type": "array",
"items": {
"type": "string",
"format": "uri"
},
"uniqueItems": true
},
"publicKey": {
"type": "array",
"items": {
Expand Down
3 changes: 3 additions & 0 deletions pkg/doc/did/testdata/valid_doc.jsonld
@@ -1,6 +1,9 @@
{
"@context": ["https://www.w3.org/ns/did/v1"],
"id": "did:example:21tDAKCERh95uGgKbJNHYp",
"alsoKnownAs": [
"did:example:123"
],
"verificationMethod": [
{
"id": "did:example:123456789abcdefghi#keys-1",
Expand Down
3 changes: 3 additions & 0 deletions pkg/doc/did/testdata/valid_doc_resolution.jsonld
Expand Up @@ -5,6 +5,9 @@
"https://w3id.org/did/v1"
],
"id": "did:example:21tDAKCERh95uGgKbJNHYp",
"alsoKnownAs": [
"did:example:123"
],
"verificationMethod": [
{
"id": "did:example:123456789abcdefghi#keys-1",
Expand Down
3 changes: 3 additions & 0 deletions pkg/doc/did/testdata/valid_doc_v0.11.jsonld
@@ -1,6 +1,9 @@
{
"@context": ["https://w3id.org/did/v0.11"],
"id": "did:example:21tDAKCERh95uGgKbJNHYp",
"alsoKnownAs": [
"did:example:123"
],
"publicKey": [
{
"id": "did:example:123456789abcdefghi#keys-1",
Expand Down
3 changes: 3 additions & 0 deletions pkg/doc/did/testdata/valid_doc_with_base.jsonld
Expand Up @@ -2,6 +2,9 @@
"@context": ["https://www.w3.org/ns/did/v1",
{ "@base": "did:example:123456789abcdefghi"}],
"id": "did:example:123456789abcdefghi",
"alsoKnownAs": [
"did:example:123"
],
"verificationMethod": [
{
"id": "#keys-1",
Expand Down

0 comments on commit 6f14536

Please sign in to comment.