ostatus2 0.1.0 → 0.1.1
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/README.md +1 -3
- data/lib/ostatus2/publication.rb +23 -0
- data/lib/ostatus2/salmon.rb +85 -0
- data/lib/ostatus2/subscription.rb +62 -0
- data/lib/ostatus2/version.rb +3 -0
- data/lib/ostatus2.rb +18 -0
- metadata +11 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 53864441f9a3c0a536a52f316390630d8982ccbf
|
4
|
+
data.tar.gz: c8699fed6438b98e12c82075c3aecdd3ca98bf6c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0f5b426461d8508c31d82fc40253eddaaf06d45ed848324edfdc96186d28b71b2dd1bb6a62973659bbfbd5fa613698fce6a04f9336159d4733f74037e3ecfca1
|
7
|
+
data.tar.gz: 488bd014fe2b8d546e28e191eb48a07a3b9805e1e81020f958bfbebef21eded45fb654d3d205d941b38f93cde60155ff73051f8a0288d0b97ba85037a728cdee
|
data/README.md
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
OStatus2
|
2
|
-
|
2
|
+
==========
|
3
3
|
|
4
4
|
[][gem]
|
5
5
|
[][travis]
|
@@ -14,8 +14,6 @@ A Ruby toolset for interacting with the OStatus suite of protocols:
|
|
14
14
|
* Subscribing to and publishing feeds via PubSubHubbub
|
15
15
|
* Interacting with feeds via Salmon
|
16
16
|
|
17
|
-
The 2 in the name is not a reference to any protocol version. There used to be a different Ruby gem called "ostatus" which this one seeks to replace.
|
18
|
-
|
19
17
|
## Installation
|
20
18
|
|
21
19
|
gem install ostatus2
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module OStatus2
|
2
|
+
class Publication
|
3
|
+
# @param [String] url Topic URL
|
4
|
+
# @param [Array] hubs URLs of the hubs which should be notified about the update
|
5
|
+
def initialize(url, hubs = [])
|
6
|
+
@url = url
|
7
|
+
@hubs = hubs.map { |hub_url| Addressable::URI.parse(hub_url) }
|
8
|
+
end
|
9
|
+
|
10
|
+
# Notifies hubs about the update to the topic URL
|
11
|
+
# @raise [HTTP::Error] Error raised upon delivery failure
|
12
|
+
# @raise [OpenSSL::SSL::SSLError] Error raised upon SSL-related failure during delivery
|
13
|
+
def publish
|
14
|
+
@hubs.each { |hub| http_client.post(hub, form: { 'hub.mode' => 'publish', 'hub.url' => @url }) }
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def http_client
|
20
|
+
HTTP
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module OStatus2
|
2
|
+
class Salmon
|
3
|
+
XMLNS = 'http://salmon-protocol.org/ns/magic-env'
|
4
|
+
|
5
|
+
# Create a magical envelope XML document around the original body
|
6
|
+
# and sign it with a private key
|
7
|
+
# @param [String] body
|
8
|
+
# @param [OpenSSL::PKey::RSA] key The private part of the key will be used
|
9
|
+
# @return [String] Magical envelope XML
|
10
|
+
def pack(body, key)
|
11
|
+
signed = plaintext_signature(body, 'application/atom+xml', 'base64url', 'RSA-SHA256')
|
12
|
+
signature = Base64.urlsafe_encode64(key.sign(digest, signed))
|
13
|
+
|
14
|
+
Nokogiri::XML::Builder.new do |xml|
|
15
|
+
xml['me'].env({ 'xmlns:me' => XMLNS }) do
|
16
|
+
xml['me'].data({ type: 'application/atom+xml' }, Base64.urlsafe_encode64(body))
|
17
|
+
xml['me'].encoding('base64url')
|
18
|
+
xml['me'].alg('RSA-SHA256')
|
19
|
+
xml['me'].sig({ key_id: Base64.urlsafe_encode64(key.public_key.to_s) }, signature)
|
20
|
+
end
|
21
|
+
end.to_xml
|
22
|
+
end
|
23
|
+
|
24
|
+
# Deliver the magical envelope to a Salmon endpoint
|
25
|
+
# @param [String] salmon_url Salmon endpoint URL
|
26
|
+
# @param [String] envelope Magical envelope
|
27
|
+
# @raise [HTTP::Error] Error raised upon delivery failure
|
28
|
+
# @raise [OpenSSL::SSL::SSLError] Error raised upon SSL-related failure during delivery
|
29
|
+
# @return [HTTP::Response]
|
30
|
+
def post(salmon_url, envelope)
|
31
|
+
http_client.headers(HTTP::Headers::CONTENT_TYPE => 'application/magic-envelope+xml').post(Addressable::URI.parse(salmon_url), body: envelope)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Unpack a magical envelope to get the content inside
|
35
|
+
# @param [String] raw_body Magical envelope
|
36
|
+
# @raise [OStatus2::BadSalmonError] Error raised if the envelope is malformed
|
37
|
+
# @return [String] Content inside the envelope
|
38
|
+
def unpack(raw_body)
|
39
|
+
body, _, _ = parse(raw_body)
|
40
|
+
body
|
41
|
+
end
|
42
|
+
|
43
|
+
# Verify the magical envelope's integrity
|
44
|
+
# @param [String] raw_body Magical envelope
|
45
|
+
# @param [OpenSSL::PKey::RSA] key The public part of the key will be used
|
46
|
+
# @return [Boolean]
|
47
|
+
def verify(raw_body, key)
|
48
|
+
_, plaintext, signature = parse(raw_body)
|
49
|
+
key.public_key.verify(digest, signature, plaintext)
|
50
|
+
rescue OStatus2::BadSalmonError
|
51
|
+
false
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def http_client
|
57
|
+
HTTP
|
58
|
+
end
|
59
|
+
|
60
|
+
def digest
|
61
|
+
OpenSSL::Digest::SHA256.new
|
62
|
+
end
|
63
|
+
|
64
|
+
def parse(raw_body)
|
65
|
+
xml = Nokogiri::XML(raw_body)
|
66
|
+
|
67
|
+
raise OStatus2::BadSalmonError if xml.at_xpath('//me:data').nil? || xml.at_xpath('//me:data').attribute('type').nil? || xml.at_xpath('//me:sig').nil? || xml.at_xpath('//me:encoding').nil? || xml.at_xpath('//me:alg').nil?
|
68
|
+
|
69
|
+
data = xml.at_xpath('//me:data')
|
70
|
+
type = data.attribute('type').value
|
71
|
+
body = Base64::urlsafe_decode64(data.content)
|
72
|
+
sig = xml.at_xpath('//me:sig')
|
73
|
+
signature = Base64::urlsafe_decode64(sig.content)
|
74
|
+
encoding = xml.at_xpath('//me:encoding').content
|
75
|
+
alg = xml.at_xpath('//me:alg').content
|
76
|
+
plaintext = plaintext_signature(body, type, encoding, alg)
|
77
|
+
|
78
|
+
[body, plaintext, signature]
|
79
|
+
end
|
80
|
+
|
81
|
+
def plaintext_signature(data, type, encoding, alg)
|
82
|
+
[data, type, encoding, alg].map { |i| Base64.urlsafe_encode64(i) }.join('.')
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module OStatus2
|
2
|
+
class Subscription
|
3
|
+
# @param [String] topic_url The URL of the topic of the subscription
|
4
|
+
# @param [Hash] options
|
5
|
+
# @option options [String] :webhook Webhook URL
|
6
|
+
# @option options [String] :hub Hub URL to work with
|
7
|
+
# @option options [String] :secret Secret key of the subscription
|
8
|
+
# @option options [String] :token Verification token of the subscription
|
9
|
+
def initialize(topic_url, options = {})
|
10
|
+
@topic_url = topic_url
|
11
|
+
@webhook_url = options[:webhook] || ''
|
12
|
+
@secret = options[:secret] || ''
|
13
|
+
@token = options[:token] || ''
|
14
|
+
@hub = options[:hub] || ''
|
15
|
+
end
|
16
|
+
|
17
|
+
# Subscribe to the topic via a specified hub
|
18
|
+
# @raise [HTTP::Error] Error raised upon delivery failure
|
19
|
+
# @raise [OpenSSL::SSL::SSLError] Error raised upon SSL-related failure during delivery
|
20
|
+
# @return [Boolean]
|
21
|
+
def subscribe
|
22
|
+
update_subscription(:subscribe)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Unsubscribe from the topic via a specified hub
|
26
|
+
# @raise [HTTP::Error] Error raised upon delivery failure
|
27
|
+
# @raise [OpenSSL::SSL::SSLError] Error raised upon SSL-related failure during delivery
|
28
|
+
# @return [Boolean]
|
29
|
+
def unsubscribe
|
30
|
+
update_subscription(:unsubscribe)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Check if the hub is responding to the right subscription request
|
34
|
+
# @param [String] topic_url A hub.topic from the hub
|
35
|
+
# @param [String] token A hub.verify_token from the hub
|
36
|
+
# @return [Boolean]
|
37
|
+
def valid?(topic_url, token)
|
38
|
+
@topic_url == topic_url && @token == token
|
39
|
+
end
|
40
|
+
|
41
|
+
# Verify that the feed contents were meant for this subscription
|
42
|
+
# @param [String] content
|
43
|
+
# @param [String] signature
|
44
|
+
# @return [Boolean]
|
45
|
+
def verify(content, signature)
|
46
|
+
hmac = OpenSSL::HMAC.hexdigest('sha1', @secret, content)
|
47
|
+
signature == "sha1=#{hmac}"
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def update_subscription(mode)
|
53
|
+
hub_url = Addressable::URI.parse(@hub)
|
54
|
+
response = http_client.post(hub_url, form: { 'hub.mode' => mode.to_s, 'hub.callback' => @webhook_url, 'hub.verify' => 'async', 'hub.verify_token' => @token, 'hub.lease_seconds' => '', 'hub.secret' => @secret, 'hub.topic' => @topic_url })
|
55
|
+
response.code == 200
|
56
|
+
end
|
57
|
+
|
58
|
+
def http_client
|
59
|
+
HTTP
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/lib/ostatus2.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'openssl'
|
3
|
+
require 'http'
|
4
|
+
require 'addressable'
|
5
|
+
require 'nokogiri'
|
6
|
+
|
7
|
+
require 'ostatus2/version'
|
8
|
+
require 'ostatus2/publication'
|
9
|
+
require 'ostatus2/subscription'
|
10
|
+
require 'ostatus2/salmon'
|
11
|
+
|
12
|
+
module OStatus2
|
13
|
+
class Error < StandardError
|
14
|
+
end
|
15
|
+
|
16
|
+
class BadSalmonError < Error
|
17
|
+
end
|
18
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ostatus2
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Eugen Rochko
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-02-
|
11
|
+
date: 2016-02-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: http
|
@@ -58,15 +58,15 @@ dependencies:
|
|
58
58
|
requirements:
|
59
59
|
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: '1.
|
61
|
+
version: '1.3'
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: '1.
|
69
|
-
description: Toolset for interacting with the
|
68
|
+
version: '1.3'
|
69
|
+
description: Toolset for interacting with the OStatus2 suite of protocols
|
70
70
|
email:
|
71
71
|
- eugen@zeonfederated.com
|
72
72
|
executables: []
|
@@ -74,6 +74,11 @@ extensions: []
|
|
74
74
|
extra_rdoc_files: []
|
75
75
|
files:
|
76
76
|
- README.md
|
77
|
+
- lib/ostatus2.rb
|
78
|
+
- lib/ostatus2/publication.rb
|
79
|
+
- lib/ostatus2/salmon.rb
|
80
|
+
- lib/ostatus2/subscription.rb
|
81
|
+
- lib/ostatus2/version.rb
|
77
82
|
homepage: https://github.com/Gargron/ostatus2
|
78
83
|
licenses:
|
79
84
|
- MIT
|
@@ -97,6 +102,6 @@ rubyforge_project:
|
|
97
102
|
rubygems_version: 2.4.6
|
98
103
|
signing_key:
|
99
104
|
specification_version: 4
|
100
|
-
summary: Toolset for interacting with the
|
105
|
+
summary: Toolset for interacting with the OStatus2 suite of protocols
|
101
106
|
test_files: []
|
102
107
|
has_rdoc:
|