Merge pull request #65 from SuperTux88/refactor-relayable-signatures
Refactor relayable signatures
This commit is contained in:
commit
7a28db7d76
13 changed files with 62 additions and 143 deletions
|
|
@ -16,7 +16,6 @@ See also: [Relayable][relayable]
|
||||||
| `text` | [Markdown][markdown] (65535) | The comment text. |
|
| `text` | [Markdown][markdown] (65535) | The comment text. |
|
||||||
| `created_at` | [Timestamp][timestamp] | The create timestamp of the comment. |
|
| `created_at` | [Timestamp][timestamp] | The create timestamp of the comment. |
|
||||||
| `author_signature` | [Signature][signature] | The signature from the author of the comment. |
|
| `author_signature` | [Signature][signature] | The signature from the author of the comment. |
|
||||||
| `parent_author_signature` | [Signature][signature] | The signature from the parent entity author. |
|
|
||||||
|
|
||||||
## Optional Properties
|
## Optional Properties
|
||||||
|
|
||||||
|
|
@ -26,8 +25,6 @@ See also: [Relayable][relayable]
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
### From author
|
|
||||||
|
|
||||||
~~~xml
|
~~~xml
|
||||||
<comment>
|
<comment>
|
||||||
<author>alice@example.org</author>
|
<author>alice@example.org</author>
|
||||||
|
|
@ -36,21 +33,6 @@ See also: [Relayable][relayable]
|
||||||
<parent_guid>c3893bf029e7013487753131731751e9</parent_guid>
|
<parent_guid>c3893bf029e7013487753131731751e9</parent_guid>
|
||||||
<text>this is a very informative comment</text>
|
<text>this is a very informative comment</text>
|
||||||
<author_signature>cGIsxB5hU/94+rmgIg/Z+OUvXVYcY/kMOvc267ybpk1pT44P1JiWfnI26F1Mta62UjzIW/SjeAO0RIsJRguaISLpXX/d5DJCMpePAZaZiagUbdgH/w4L++fXiPxBKkSm+PB4txxmHGN8FHjwEUJFHJ1m3VfU4w2JC8+IBU93eag=</author_signature>
|
<author_signature>cGIsxB5hU/94+rmgIg/Z+OUvXVYcY/kMOvc267ybpk1pT44P1JiWfnI26F1Mta62UjzIW/SjeAO0RIsJRguaISLpXX/d5DJCMpePAZaZiagUbdgH/w4L++fXiPxBKkSm+PB4txxmHGN8FHjwEUJFHJ1m3VfU4w2JC8+IBU93eag=</author_signature>
|
||||||
<parent_author_signature/>
|
|
||||||
</comment>
|
|
||||||
~~~
|
|
||||||
|
|
||||||
### From parent author
|
|
||||||
|
|
||||||
~~~xml
|
|
||||||
<comment>
|
|
||||||
<author>alice@example.org</author>
|
|
||||||
<guid>5c241a3029f8013487763131731751e9</guid>
|
|
||||||
<created_at>2016-07-12T00:49:06Z</created_at>
|
|
||||||
<parent_guid>c3893bf029e7013487753131731751e9</parent_guid>
|
|
||||||
<text>this is a very informative comment</text>
|
|
||||||
<author_signature>cGIsxB5hU/94+rmgIg/Z+OUvXVYcY/kMOvc267ybpk1pT44P1JiWfnI26F1Mta62UjzIW/SjeAO0RIsJRguaISLpXX/d5DJCMpePAZaZiagUbdgH/w4L++fXiPxBKkSm+PB4txxmHGN8FHjwEUJFHJ1m3VfU4w2JC8+IBU93eag=</author_signature>
|
|
||||||
<parent_author_signature>uzjxUSqR8DQBSBa6abY7R/s9DVzT6UAgTctRcUu5rV5o0iXJD2MR6kp6bsVH3nMbbNvOjwAtrdfz3SVHT2gD8M5PmoFagxK7m5T1c9FB0i+wknoAah0Si0c2sP/BPLnkQ83DgLjF+JZCzMX4sWKvYfyaMjnih1MtQILGyuiwA0E=</parent_author_signature>
|
|
||||||
</comment>
|
</comment>
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,12 +21,9 @@ See also: [Relayable][relayable]
|
||||||
| `parent_type` | [Type][type] | The entity type of the parent. |
|
| `parent_type` | [Type][type] | The entity type of the parent. |
|
||||||
| `positive` | [Boolean][boolean] | `true` if it is a like, `false` if it is a dislike. |
|
| `positive` | [Boolean][boolean] | `true` if it is a like, `false` if it is a dislike. |
|
||||||
| `author_signature` | [Signature][signature] | The signature from the author of the like. |
|
| `author_signature` | [Signature][signature] | The signature from the author of the like. |
|
||||||
| `parent_author_signature` | [Signature][signature] | The signature from the parent entity author. |
|
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
### From author
|
|
||||||
|
|
||||||
~~~xml
|
~~~xml
|
||||||
<like>
|
<like>
|
||||||
<positive>true</positive>
|
<positive>true</positive>
|
||||||
|
|
@ -35,21 +32,6 @@ See also: [Relayable][relayable]
|
||||||
<parent_guid>947a854029f7013487753131731751e9</parent_guid>
|
<parent_guid>947a854029f7013487753131731751e9</parent_guid>
|
||||||
<author>alice@example.org</author>
|
<author>alice@example.org</author>
|
||||||
<author_signature>gk8e+K7XRjVRblv8B8PVOf7BpURbf5HrXO5rmq8D/AkPO7lA0+Akwouu5JGKAHIhPR3dfXVp0o6bIDD+e8gtMYRdDd5IHRfBGNk3WsQecnbhmesHy40Qca/dCQcdcXd5aeWHJKeyUrSAvS55U6VUpk/DK/4IIEZfnr0T9+jM8I0=</author_signature>
|
<author_signature>gk8e+K7XRjVRblv8B8PVOf7BpURbf5HrXO5rmq8D/AkPO7lA0+Akwouu5JGKAHIhPR3dfXVp0o6bIDD+e8gtMYRdDd5IHRfBGNk3WsQecnbhmesHy40Qca/dCQcdcXd5aeWHJKeyUrSAvS55U6VUpk/DK/4IIEZfnr0T9+jM8I0=</author_signature>
|
||||||
<parent_author_signature/>
|
|
||||||
</like>
|
|
||||||
~~~
|
|
||||||
|
|
||||||
### From parent author
|
|
||||||
|
|
||||||
~~~xml
|
|
||||||
<like>
|
|
||||||
<positive>true</positive>
|
|
||||||
<guid>947a88f029f7013487753131731751e9</guid>
|
|
||||||
<parent_type>Post</parent_type>
|
|
||||||
<parent_guid>947a854029f7013487753131731751e9</parent_guid>
|
|
||||||
<author>alice@example.org</author>
|
|
||||||
<author_signature>gk8e+K7XRjVRblv8B8PVOf7BpURbf5HrXO5rmq8D/AkPO7lA0+Akwouu5JGKAHIhPR3dfXVp0o6bIDD+e8gtMYRdDd5IHRfBGNk3WsQecnbhmesHy40Qca/dCQcdcXd5aeWHJKeyUrSAvS55U6VUpk/DK/4IIEZfnr0T9+jM8I0=</author_signature>
|
|
||||||
<parent_author_signature>0oAjHO8uIn2Z3Gcmo1KF8su0c7bqI6MzTRq5JagGaZVkFVU8WlNqtwamu6xlmpcAoClGpI5xvbnHzyw5YA8NS8KmUy8BUpg67Mq4QsHHBtueNxHuhgRjszN2V0S8BUKFHGJnnvXmZ/P6YGOOomDgp9I/7zIOownvIm5wj2MotWw=</parent_author_signature>
|
|
||||||
</like>
|
</like>
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,12 +15,9 @@ See also: [Relayable][relayable]
|
||||||
| `parent_guid` | [GUID][guid] | The GUID of the [Poll][poll]. |
|
| `parent_guid` | [GUID][guid] | The GUID of the [Poll][poll]. |
|
||||||
| `poll_answer_guid` | [GUID][guid] | The GUID of the [PollAnswer][poll_answer]. |
|
| `poll_answer_guid` | [GUID][guid] | The GUID of the [PollAnswer][poll_answer]. |
|
||||||
| `author_signature` | [Signature][signature] | The signature from the author of the poll participation. |
|
| `author_signature` | [Signature][signature] | The signature from the author of the poll participation. |
|
||||||
| `parent_author_signature` | [Signature][signature] | The signature from the author of the [Poll][poll]. |
|
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
### From author
|
|
||||||
|
|
||||||
~~~xml
|
~~~xml
|
||||||
<poll_participation>
|
<poll_participation>
|
||||||
<guid>f1eb866029f7013487753131731751e9</guid>
|
<guid>f1eb866029f7013487753131731751e9</guid>
|
||||||
|
|
@ -28,20 +25,6 @@ See also: [Relayable][relayable]
|
||||||
<author>alice@example.org</author>
|
<author>alice@example.org</author>
|
||||||
<poll_answer_guid>2a22db2029e9013487753131731751e9</poll_answer_guid>
|
<poll_answer_guid>2a22db2029e9013487753131731751e9</poll_answer_guid>
|
||||||
<author_signature>dT6KbT7kp0bE+s3//ZErxO1wvVIqtD0lY67i81+dO43B4D2m5kjCdzW240eWt/jZmcHIsdxXf4WHNdrb6ZDnamA8I1FUVnLjHA9xexBITQsSLXrcV88UdammSmmOxl1Ac4VUXqFpdavm6a7/MwOJ7+JHP8TbUO9siN+hMfgUbtY=</author_signature>
|
<author_signature>dT6KbT7kp0bE+s3//ZErxO1wvVIqtD0lY67i81+dO43B4D2m5kjCdzW240eWt/jZmcHIsdxXf4WHNdrb6ZDnamA8I1FUVnLjHA9xexBITQsSLXrcV88UdammSmmOxl1Ac4VUXqFpdavm6a7/MwOJ7+JHP8TbUO9siN+hMfgUbtY=</author_signature>
|
||||||
<parent_author_signature/>
|
|
||||||
</poll_participation>
|
|
||||||
~~~
|
|
||||||
|
|
||||||
### From parent author
|
|
||||||
|
|
||||||
~~~xml
|
|
||||||
<poll_participation>
|
|
||||||
<guid>f1eb866029f7013487753131731751e9</guid>
|
|
||||||
<parent_guid>2a22d6c029e9013487753131731751e9</parent_guid>
|
|
||||||
<author>alice@example.org</author>
|
|
||||||
<poll_answer_guid>2a22db2029e9013487753131731751e9</poll_answer_guid>
|
|
||||||
<author_signature>dT6KbT7kp0bE+s3//ZErxO1wvVIqtD0lY67i81+dO43B4D2m5kjCdzW240eWt/jZmcHIsdxXf4WHNdrb6ZDnamA8I1FUVnLjHA9xexBITQsSLXrcV88UdammSmmOxl1Ac4VUXqFpdavm6a7/MwOJ7+JHP8TbUO9siN+hMfgUbtY=</author_signature>
|
|
||||||
<parent_author_signature>gWasNPpSnMcKBIMWyzfoVO6sr8eRYkhUqy3PIkkh53n/ki+DM9mnh3ayotI0+6un9aq1N3XkS7Vn05ZD3+nHVby6i21XkYgPnbD8pWYuBBj7VGPyahT70BUs/vSvY8KX8V3wYfsPsaiAgJsAFg2UHYdY3r4/oWdIIbBZc21O3zk=</parent_author_signature>
|
|
||||||
</poll_participation>
|
</poll_participation>
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,24 +21,25 @@ All relayables have the following properties:
|
||||||
| `guid` | [GUID][guid] | The GUID of the relayable. |
|
| `guid` | [GUID][guid] | The GUID of the relayable. |
|
||||||
| `parent_guid` | [GUID][guid] | The GUID of the parent entity. |
|
| `parent_guid` | [GUID][guid] | The GUID of the parent entity. |
|
||||||
| `author_signature` | [Signature][signature] | The signature from the author of the relayable. |
|
| `author_signature` | [Signature][signature] | The signature from the author of the relayable. |
|
||||||
| `parent_author_signature` | [Signature][signature] | The signature from the parent entity author. |
|
|
||||||
|
|
||||||
## Relaying
|
## Relaying
|
||||||
|
|
||||||
The author of the relayable sends the entity to the parent author. The author must include the `author_signature`. The
|
If the author is not the same as the parent author, the author of the relayable sends the entity to the parent author
|
||||||
`parent_author_signature` may be empty or missing.
|
and the author must include the `author_signature`.
|
||||||
|
|
||||||
The parent author then must add the `parent_author_signature` and send the entity to all the recipients of the parent
|
The parent author then must envelop it in a new [Magic Envelope][magicsig] and send the entity to all the recipients
|
||||||
entity.
|
of the parent entity. If the author and the parent author are on the same server, the author must sign the
|
||||||
|
`author_signature` and the parent author needs to sign the Magic Envelope.
|
||||||
|
|
||||||
If someone other then the parent author receives an relayable without a valid `parent_author_signature`, it must be
|
If someone other then the parent author receives a relayable without a valid Magic Envelope signed from
|
||||||
ignored. If the `author_signature` is missing or invalid, it also must be ignored.
|
the parent author, it must be ignored. If the author is not the same as the parent author and the `author_signature`
|
||||||
|
is missing or invalid, it also must be ignored. If the author is the same as the parent author, the `author_signature`
|
||||||
|
can be missing, because a valid signature in the Magic Envelope from the author is enough in that case.
|
||||||
|
|
||||||
## Signatures
|
## Signatures
|
||||||
|
|
||||||
The string to sign is built with the content of all properties (except the `author_signature` and
|
The string to sign is built with the content of all properties (except the `author_signature` itself),
|
||||||
`parent_author_signature` itself), concatenated using `;` as separator in the same order as they appear in the XML. The
|
concatenated using `;` as separator in the same order as they appear in the XML. The order in the XML is not specified.
|
||||||
order in the XML is not specified.
|
|
||||||
|
|
||||||
This ensures that relayables even work, if the parent author or another recipient does not know all properties of the
|
This ensures that relayables even work, if the parent author or another recipient does not know all properties of the
|
||||||
relayable entity (e.g. older version of diaspora\*).
|
relayable entity (e.g. older version of diaspora\*).
|
||||||
|
|
|
||||||
|
|
@ -28,16 +28,15 @@ module DiasporaFederation
|
||||||
# @return [String] parent guid
|
# @return [String] parent guid
|
||||||
#
|
#
|
||||||
# @!attribute [r] author_signature
|
# @!attribute [r] author_signature
|
||||||
# Contains a signature of the entity using the private key of the author of a post itself
|
# Contains a signature of the entity using the private key of the author of a relayable itself.
|
||||||
# The presence of this signature is mandatory. Without it the entity won't be accepted by
|
# The presence of this signature is mandatory. Without it the entity won't be accepted by
|
||||||
# a target pod.
|
# a target pod.
|
||||||
# @return [String] author signature
|
# @return [String] author signature
|
||||||
#
|
#
|
||||||
# @!attribute [r] parent_author_signature
|
# @!attribute [r] parent_author_signature
|
||||||
# Contains a signature of the entity using the private key of the author of a parent post
|
# Contains a signature of the entity using the private key of the author of a parent post.
|
||||||
# This signature is required only when federating from upstream (parent) post author to
|
# @deprecated This signature isn't required anymore, because we can check the signature from
|
||||||
# downstream subscribers. This is the case when the parent author has to resend a relayable
|
# the parent author in the MagicEnvelope.
|
||||||
# received from one of their subscribers to all others.
|
|
||||||
# @return [String] parent author signature
|
# @return [String] parent author signature
|
||||||
#
|
#
|
||||||
# @!attribute [r] parent
|
# @!attribute [r] parent
|
||||||
|
|
@ -71,18 +70,17 @@ module DiasporaFederation
|
||||||
super(data)
|
super(data)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Verifies the signatures (+author_signature+ and +parent_author_signature+ if needed).
|
# Verifies the +author_signature+ if needed.
|
||||||
|
# @see DiasporaFederation::Entities::Signable#verify_signature
|
||||||
|
#
|
||||||
# @raise [SignatureVerificationFailed] if the signature is not valid
|
# @raise [SignatureVerificationFailed] if the signature is not valid
|
||||||
# @raise [PublicKeyNotFound] if no public key is found
|
# @raise [PublicKeyNotFound] if no public key is found
|
||||||
def verify_signatures
|
def verify_signature
|
||||||
verify_signature(author, :author_signature)
|
super(author, :author_signature) unless author == parent.author
|
||||||
|
|
||||||
# This happens only on downstream federation.
|
|
||||||
verify_signature(parent.author, :parent_author_signature) unless parent.local
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def sender_valid?(sender)
|
def sender_valid?(sender)
|
||||||
sender == author || sender == parent.author
|
(sender == author && parent.local) || sender == parent.author
|
||||||
end
|
end
|
||||||
|
|
||||||
# @return [String] string representation of this object
|
# @return [String] string representation of this object
|
||||||
|
|
@ -136,7 +134,7 @@ module DiasporaFederation
|
||||||
def enriched_properties
|
def enriched_properties
|
||||||
super.merge(additional_data).tap do |hash|
|
super.merge(additional_data).tap do |hash|
|
||||||
hash[:author_signature] = author_signature || sign_with_author
|
hash[:author_signature] = author_signature || sign_with_author
|
||||||
hash[:parent_author_signature] = parent_author_signature || sign_with_parent_author_if_available.to_s
|
hash.delete(:parent_author_signature)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -144,7 +142,9 @@ module DiasporaFederation
|
||||||
#
|
#
|
||||||
# @return [Hash] sorted xml elements
|
# @return [Hash] sorted xml elements
|
||||||
def xml_elements
|
def xml_elements
|
||||||
data = super
|
data = super.tap do |hash|
|
||||||
|
hash[:parent_author_signature] = parent_author_signature || sign_with_parent_author_if_available.to_s
|
||||||
|
end
|
||||||
order = signature_order + %i(author_signature parent_author_signature)
|
order = signature_order + %i(author_signature parent_author_signature)
|
||||||
order.map {|element| [element, data[element] || ""] }.to_h
|
order.map {|element| [element, data[element] || ""] }.to_h
|
||||||
end
|
end
|
||||||
|
|
@ -178,7 +178,7 @@ module DiasporaFederation
|
||||||
additional_data = properties_hash.reject {|key, _| class_props.has_key?(key) }
|
additional_data = properties_hash.reject {|key, _| class_props.has_key?(key) }
|
||||||
|
|
||||||
fetch_parent(properties_hash)
|
fetch_parent(properties_hash)
|
||||||
new(properties_hash, property_order, additional_data).tap(&:verify_signatures)
|
new(properties_hash, property_order, additional_data).tap(&:verify_signature)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
|
||||||
|
|
@ -46,8 +46,7 @@
|
||||||
"author": { "type": "string" },
|
"author": { "type": "string" },
|
||||||
"guid": { "$ref": "#/definitions/guid" },
|
"guid": { "$ref": "#/definitions/guid" },
|
||||||
"parent_guid": { "$ref": "#/definitions/guid" },
|
"parent_guid": { "$ref": "#/definitions/guid" },
|
||||||
"author_signature": { "$ref": "#/definitions/signature" },
|
"author_signature": { "$ref": "#/definitions/signature" }
|
||||||
"parent_author_signature": { "$ref": "#/definitions/signature" }
|
|
||||||
},
|
},
|
||||||
"required": [
|
"required": [
|
||||||
"author", "guid", "parent_guid"
|
"author", "guid", "parent_guid"
|
||||||
|
|
|
||||||
|
|
@ -224,7 +224,6 @@ XML
|
||||||
|
|
||||||
before do
|
before do
|
||||||
expect_callback(:fetch_public_key, author).and_return(author_key.public_key)
|
expect_callback(:fetch_public_key, author).and_return(author_key.public_key)
|
||||||
expect_callback(:fetch_public_key, parent.author).and_return(parent_key.public_key)
|
|
||||||
expect_callback(:fetch_related_entity, "Post", parent_guid).and_return(parent)
|
expect_callback(:fetch_related_entity, "Post", parent_guid).and_return(parent)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,6 @@ XML
|
||||||
"guid": "#{data[:guid]}",
|
"guid": "#{data[:guid]}",
|
||||||
"parent_guid": "#{parent.guid}",
|
"parent_guid": "#{parent.guid}",
|
||||||
"author_signature": "#{data[:author_signature]}",
|
"author_signature": "#{data[:author_signature]}",
|
||||||
"parent_author_signature": "#{data[:parent_author_signature]}",
|
|
||||||
"text": "#{data[:text]}",
|
"text": "#{data[:text]}",
|
||||||
"created_at": "#{data[:created_at].iso8601}"
|
"created_at": "#{data[:created_at].iso8601}"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,6 @@ XML
|
||||||
"guid": "#{data[:guid]}",
|
"guid": "#{data[:guid]}",
|
||||||
"parent_guid": "#{parent.guid}",
|
"parent_guid": "#{parent.guid}",
|
||||||
"author_signature": "#{data[:author_signature]}",
|
"author_signature": "#{data[:author_signature]}",
|
||||||
"parent_author_signature": "#{data[:parent_author_signature]}",
|
|
||||||
"parent_type": "#{parent.entity_type}",
|
"parent_type": "#{parent.entity_type}",
|
||||||
"positive": #{data[:positive]}
|
"positive": #{data[:positive]}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,6 @@ XML
|
||||||
"guid": "#{data[:guid]}",
|
"guid": "#{data[:guid]}",
|
||||||
"parent_guid": "#{parent.guid}",
|
"parent_guid": "#{parent.guid}",
|
||||||
"author_signature": "#{data[:author_signature]}",
|
"author_signature": "#{data[:author_signature]}",
|
||||||
"parent_author_signature": "#{data[:parent_author_signature]}",
|
|
||||||
"poll_answer_guid": "#{data[:poll_answer_guid]}"
|
"poll_answer_guid": "#{data[:poll_answer_guid]}"
|
||||||
},
|
},
|
||||||
"property_order": [
|
"property_order": [
|
||||||
|
|
|
||||||
|
|
@ -25,16 +25,15 @@ module DiasporaFederation
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#verify_signatures" do
|
describe "#verify_signature" do
|
||||||
it "doesn't raise anything if correct signatures were passed" do
|
it "doesn't raise anything if correct signatures were passed" do
|
||||||
hash[:author_signature] = sign_with_key(author_pkey, signature_data)
|
hash[:author_signature] = sign_with_key(author_pkey, signature_data)
|
||||||
hash[:parent_author_signature] = sign_with_key(parent_pkey, signature_data)
|
hash[:parent_author_signature] = sign_with_key(parent_pkey, signature_data)
|
||||||
hash[:parent] = remote_parent
|
hash[:parent] = remote_parent
|
||||||
|
|
||||||
expect_callback(:fetch_public_key, author).and_return(author_pkey.public_key)
|
expect_callback(:fetch_public_key, author).and_return(author_pkey.public_key)
|
||||||
expect_callback(:fetch_public_key, remote_parent.author).and_return(parent_pkey.public_key)
|
|
||||||
|
|
||||||
expect { Entities::SomeRelayable.new(hash, signature_order).verify_signatures }.not_to raise_error
|
expect { Entities::SomeRelayable.new(hash, signature_order).verify_signature }.not_to raise_error
|
||||||
end
|
end
|
||||||
|
|
||||||
it "doesn't raise anything if correct signatures with new property were passed" do
|
it "doesn't raise anything if correct signatures with new property were passed" do
|
||||||
|
|
@ -46,10 +45,9 @@ module DiasporaFederation
|
||||||
hash[:parent] = remote_parent
|
hash[:parent] = remote_parent
|
||||||
|
|
||||||
expect_callback(:fetch_public_key, author).and_return(author_pkey.public_key)
|
expect_callback(:fetch_public_key, author).and_return(author_pkey.public_key)
|
||||||
expect_callback(:fetch_public_key, remote_parent.author).and_return(parent_pkey.public_key)
|
|
||||||
|
|
||||||
expect {
|
expect {
|
||||||
Entities::SomeRelayable.new(hash, signature_order, "new_property" => new_property).verify_signatures
|
Entities::SomeRelayable.new(hash, signature_order, "new_property" => new_property).verify_signature
|
||||||
}.not_to raise_error
|
}.not_to raise_error
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -57,7 +55,7 @@ module DiasporaFederation
|
||||||
expect_callback(:fetch_public_key, anything).and_return(nil)
|
expect_callback(:fetch_public_key, anything).and_return(nil)
|
||||||
|
|
||||||
expect {
|
expect {
|
||||||
Entities::SomeRelayable.new(hash, signature_order).verify_signatures
|
Entities::SomeRelayable.new(hash, signature_order).verify_signature
|
||||||
}.to raise_error Entities::Relayable::PublicKeyNotFound
|
}.to raise_error Entities::Relayable::PublicKeyNotFound
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -67,66 +65,47 @@ module DiasporaFederation
|
||||||
expect_callback(:fetch_public_key, author).and_return(author_pkey.public_key)
|
expect_callback(:fetch_public_key, author).and_return(author_pkey.public_key)
|
||||||
|
|
||||||
expect {
|
expect {
|
||||||
Entities::SomeRelayable.new(hash, signature_order).verify_signatures
|
Entities::SomeRelayable.new(hash, signature_order).verify_signature
|
||||||
}.to raise_error Entities::Relayable::SignatureVerificationFailed
|
}.to raise_error Entities::Relayable::SignatureVerificationFailed
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "doesn't raise when no author signature was passed, but the author is also the parent author" do
|
||||||
|
hash[:author_signature] = nil
|
||||||
|
hash[:parent] = Fabricate(:related_entity, author: author, local: false)
|
||||||
|
|
||||||
|
expect {
|
||||||
|
Entities::SomeRelayable.new(hash, signature_order).verify_signature
|
||||||
|
}.not_to raise_error
|
||||||
|
end
|
||||||
|
|
||||||
it "raises when bad author signature was passed" do
|
it "raises when bad author signature was passed" do
|
||||||
hash[:author_signature] = sign_with_key(author_pkey, "bad signed string")
|
hash[:author_signature] = sign_with_key(author_pkey, "bad signed string")
|
||||||
|
|
||||||
expect_callback(:fetch_public_key, author).and_return(author_pkey.public_key)
|
expect_callback(:fetch_public_key, author).and_return(author_pkey.public_key)
|
||||||
|
|
||||||
expect {
|
expect {
|
||||||
Entities::SomeRelayable.new(hash, signature_order).verify_signatures
|
Entities::SomeRelayable.new(hash, signature_order).verify_signature
|
||||||
}.to raise_error Entities::Relayable::SignatureVerificationFailed
|
}.to raise_error Entities::Relayable::SignatureVerificationFailed
|
||||||
end
|
end
|
||||||
|
|
||||||
it "raises when no public key for parent author was fetched" do
|
it "doesn't raise when no parent author signature was passed" do
|
||||||
hash[:author_signature] = sign_with_key(author_pkey, signature_data)
|
|
||||||
hash[:parent] = remote_parent
|
|
||||||
|
|
||||||
expect_callback(:fetch_public_key, author).and_return(author_pkey.public_key)
|
|
||||||
expect_callback(:fetch_public_key, remote_parent.author).and_return(nil)
|
|
||||||
|
|
||||||
expect {
|
|
||||||
Entities::SomeRelayable.new(hash, signature_order).verify_signatures
|
|
||||||
}.to raise_error Entities::Relayable::PublicKeyNotFound
|
|
||||||
end
|
|
||||||
|
|
||||||
it "raises when no parent author signature was passed" do
|
|
||||||
hash[:author_signature] = sign_with_key(author_pkey, signature_data)
|
hash[:author_signature] = sign_with_key(author_pkey, signature_data)
|
||||||
hash[:parent_author_signature] = nil
|
hash[:parent_author_signature] = nil
|
||||||
hash[:parent] = remote_parent
|
hash[:parent] = remote_parent
|
||||||
|
|
||||||
expect_callback(:fetch_public_key, author).and_return(author_pkey.public_key)
|
expect_callback(:fetch_public_key, author).and_return(author_pkey.public_key)
|
||||||
expect_callback(:fetch_public_key, remote_parent.author).and_return(parent_pkey.public_key)
|
|
||||||
|
|
||||||
expect {
|
expect { Entities::SomeRelayable.new(hash, signature_order).verify_signature }.not_to raise_error
|
||||||
Entities::SomeRelayable.new(hash, signature_order).verify_signatures
|
|
||||||
}.to raise_error Entities::Relayable::SignatureVerificationFailed
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it "raises when bad parent author signature was passed" do
|
it "doesn't raise when no parent author signature was passed and we're on upstream federation" do
|
||||||
hash[:author_signature] = sign_with_key(author_pkey, signature_data)
|
|
||||||
hash[:parent_author_signature] = sign_with_key(parent_pkey, "bad signed string")
|
|
||||||
hash[:parent] = remote_parent
|
|
||||||
|
|
||||||
expect_callback(:fetch_public_key, author).and_return(author_pkey.public_key)
|
|
||||||
expect_callback(:fetch_public_key, remote_parent.author).and_return(parent_pkey.public_key)
|
|
||||||
|
|
||||||
expect {
|
|
||||||
Entities::SomeRelayable.new(hash, signature_order).verify_signatures
|
|
||||||
}.to raise_error Entities::Relayable::SignatureVerificationFailed
|
|
||||||
end
|
|
||||||
|
|
||||||
it "doesn't raise if parent_author_signature isn't set but we're on upstream federation" do
|
|
||||||
hash[:author_signature] = sign_with_key(author_pkey, signature_data)
|
hash[:author_signature] = sign_with_key(author_pkey, signature_data)
|
||||||
hash[:parent_author_signature] = nil
|
hash[:parent_author_signature] = nil
|
||||||
hash[:parent] = local_parent
|
hash[:parent] = local_parent
|
||||||
|
|
||||||
expect_callback(:fetch_public_key, author).and_return(author_pkey.public_key)
|
expect_callback(:fetch_public_key, author).and_return(author_pkey.public_key)
|
||||||
|
|
||||||
expect { Entities::SomeRelayable.new(hash, signature_order).verify_signatures }.not_to raise_error
|
expect { Entities::SomeRelayable.new(hash, signature_order).verify_signature }.not_to raise_error
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -228,7 +207,6 @@ XML
|
||||||
before do
|
before do
|
||||||
expect_callback(:fetch_related_entity, "Parent", parent_guid).and_return(remote_parent)
|
expect_callback(:fetch_related_entity, "Parent", parent_guid).and_return(remote_parent)
|
||||||
expect_callback(:fetch_public_key, author).and_return(author_pkey.public_key)
|
expect_callback(:fetch_public_key, author).and_return(author_pkey.public_key)
|
||||||
expect_callback(:fetch_public_key, remote_parent.author).and_return(parent_pkey.public_key)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
let(:new_signature_data) { "#{author};#{guid};#{parent_guid};#{new_property};#{property}" }
|
let(:new_signature_data) { "#{author};#{guid};#{parent_guid};#{new_property};#{property}" }
|
||||||
|
|
@ -329,25 +307,21 @@ XML
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "computes correct signatures for the entity with new unknown elements" do
|
it "computes correct author_signature for the entity with new unknown elements" do
|
||||||
expect_callback(:fetch_private_key, author).and_return(author_pkey)
|
expect_callback(:fetch_private_key, author).and_return(author_pkey)
|
||||||
expect_callback(:fetch_private_key, local_parent.author).and_return(parent_pkey)
|
|
||||||
|
|
||||||
property_order = [:author, :guid, :parent_guid, "new_property", :property]
|
property_order = [:author, :guid, :parent_guid, "new_property", :property]
|
||||||
signature_data_with_new_property = "#{author};#{guid};#{parent_guid};#{new_property};#{property}"
|
signature_data_with_new_property = "#{author};#{guid};#{parent_guid};#{new_property};#{property}"
|
||||||
|
|
||||||
json_hash = Entities::SomeRelayable.new(hash, property_order, "new_property" => new_property).to_json
|
json_hash = Entities::SomeRelayable.new(hash, property_order, "new_property" => new_property).to_json
|
||||||
author_signature = json_hash[:entity_data][:author_signature]
|
author_signature = json_hash[:entity_data][:author_signature]
|
||||||
parent_author_signature = json_hash[:entity_data][:parent_author_signature]
|
|
||||||
|
|
||||||
expect(verify_signature(author_pkey, author_signature, signature_data_with_new_property)).to be_truthy
|
expect(verify_signature(author_pkey, author_signature, signature_data_with_new_property)).to be_truthy
|
||||||
expect(verify_signature(parent_pkey, parent_author_signature, signature_data_with_new_property)).to be_truthy
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it "doesn't change signatures if they are already set" do
|
it "doesn't change author_signature if it is already set" do
|
||||||
json = Entities::SomeRelayable.new(hash_with_fake_signatures).to_json.to_json
|
json = Entities::SomeRelayable.new(hash_with_fake_signatures).to_json.to_json
|
||||||
expect(json).to include_json(entity_data: {author_signature: "aa"})
|
expect(json).to include_json(entity_data: {author_signature: "aa"})
|
||||||
expect(json).to include_json(entity_data: {parent_author_signature: "bb"})
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it "raises when author_signature not set and key isn't supplied" do
|
it "raises when author_signature not set and key isn't supplied" do
|
||||||
|
|
@ -358,12 +332,11 @@ XML
|
||||||
}.to raise_error Entities::Relayable::AuthorPrivateKeyNotFound
|
}.to raise_error Entities::Relayable::AuthorPrivateKeyNotFound
|
||||||
end
|
end
|
||||||
|
|
||||||
it "doesn't set parent_author_signature if key isn't supplied" do
|
it "doesn't contain the parent_author_signature" do
|
||||||
expect_callback(:fetch_private_key, author).and_return(author_pkey)
|
expect_callback(:fetch_private_key, author).and_return(author_pkey)
|
||||||
expect_callback(:fetch_private_key, local_parent.author).and_return(nil)
|
|
||||||
|
|
||||||
json = Entities::SomeRelayable.new(hash).to_json.to_json
|
json = Entities::SomeRelayable.new(hash).to_json
|
||||||
expect(json).to include_json(entity_data: {parent_author_signature: ""})
|
expect(json[:entity_data]).not_to include(:parent_author_signature)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -374,7 +347,6 @@ XML
|
||||||
before do
|
before do
|
||||||
expect_callback(:fetch_related_entity, "Parent", parent_guid).and_return(remote_parent)
|
expect_callback(:fetch_related_entity, "Parent", parent_guid).and_return(remote_parent)
|
||||||
expect_callback(:fetch_public_key, author).and_return(author_pkey.public_key)
|
expect_callback(:fetch_public_key, author).and_return(author_pkey.public_key)
|
||||||
expect_callback(:fetch_public_key, remote_parent.author).and_return(parent_pkey.public_key)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when properties are sorted and there is an unknown property" do
|
context "when properties are sorted and there is an unknown property" do
|
||||||
|
|
@ -475,9 +447,7 @@ XML
|
||||||
context "fetch parent" do
|
context "fetch parent" do
|
||||||
before do
|
before do
|
||||||
expect_callback(:fetch_public_key, author).and_return(author_pkey.public_key)
|
expect_callback(:fetch_public_key, author).and_return(author_pkey.public_key)
|
||||||
expect_callback(:fetch_public_key, remote_parent.author).and_return(parent_pkey.public_key)
|
|
||||||
expect_callback(:fetch_private_key, author).and_return(author_pkey)
|
expect_callback(:fetch_private_key, author).and_return(author_pkey)
|
||||||
expect_callback(:fetch_private_key, remote_parent.author).and_return(parent_pkey)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
let(:entity) { Entities::SomeRelayable.new(hash) }
|
let(:entity) { Entities::SomeRelayable.new(hash) }
|
||||||
|
|
@ -508,12 +478,18 @@ XML
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#sender_valid?" do
|
describe "#sender_valid?" do
|
||||||
it "allows author" do
|
it "allows author if the parent is local" do
|
||||||
entity = Entities::SomeRelayable.new(hash)
|
entity = Entities::SomeRelayable.new(hash)
|
||||||
|
|
||||||
expect(entity.sender_valid?(author)).to be_truthy
|
expect(entity.sender_valid?(author)).to be_truthy
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "does not allow the author if the parent is not local" do
|
||||||
|
entity = Entities::SomeRelayable.new(hash.merge(parent: remote_parent))
|
||||||
|
|
||||||
|
expect(entity.sender_valid?(author)).to be_falsey
|
||||||
|
end
|
||||||
|
|
||||||
it "allows parent author" do
|
it "allows parent author" do
|
||||||
entity = Entities::SomeRelayable.new(hash)
|
entity = Entities::SomeRelayable.new(hash)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ end
|
||||||
|
|
||||||
# signature methods
|
# signature methods
|
||||||
def add_signatures(hash, klass=described_class)
|
def add_signatures(hash, klass=described_class)
|
||||||
properties = klass.new(hash).send(:enriched_properties)
|
properties = klass.new(hash).send(:xml_elements)
|
||||||
hash[:author_signature] = properties[:author_signature]
|
hash[:author_signature] = properties[:author_signature]
|
||||||
hash[:parent_author_signature] = properties[:parent_author_signature]
|
hash[:parent_author_signature] = properties[:parent_author_signature]
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
def entity_hash_from(hash)
|
def entity_hash_from(hash)
|
||||||
|
hash.delete(:parent_author_signature)
|
||||||
hash.map {|key, value|
|
hash.map {|key, value|
|
||||||
if [String, TrueClass, FalseClass, Integer, NilClass].any? {|c| value.is_a? c }
|
if [String, TrueClass, FalseClass, Integer, NilClass].any? {|c| value.is_a? c }
|
||||||
[key, value]
|
[key, value]
|
||||||
|
|
@ -136,10 +137,9 @@ shared_examples "a JSON Entity" do
|
||||||
it "contains JSON properties for each of the entity properties with the entity_data property" do
|
it "contains JSON properties for each of the entity properties with the entity_data property" do
|
||||||
entity_data = entity_hash_from(data)
|
entity_data = entity_hash_from(data)
|
||||||
entity_data.delete(:parent)
|
entity_data.delete(:parent)
|
||||||
nested_elements = entity_data.select {|_key, value| value.is_a?(Array) || value.is_a?(Hash) }
|
nested_elements, simple_props = entity_data.partition {|_key, value| value.is_a?(Array) || value.is_a?(Hash) }
|
||||||
entity_data.reject! {|_key, value| value.is_a?(Array) || value.is_a?(Hash) }
|
|
||||||
|
|
||||||
expect(to_json_output).to include_json(entity_data: entity_data)
|
expect(to_json_output).to include_json(entity_data: simple_props.to_h)
|
||||||
nested_elements.each {|key, value|
|
nested_elements.each {|key, value|
|
||||||
type = described_class.class_props[key]
|
type = described_class.class_props[key]
|
||||||
if value.is_a?(Array)
|
if value.is_a?(Array)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue