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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 73f059441105cb19ff006d6e1e73b3e4a6f1e480
4
- data.tar.gz: 578ef3a4bedebe13ea5eb673082c24ff6ea1a8f0
3
+ metadata.gz: 53864441f9a3c0a536a52f316390630d8982ccbf
4
+ data.tar.gz: c8699fed6438b98e12c82075c3aecdd3ca98bf6c
5
5
  SHA512:
6
- metadata.gz: eb93203d24dab71975713420ed7fd17b5c03efa10aa296193800612c0f7d657d14df20bfac8d7e24c04ac5f98ceb0fc863f8058e1fbe3160e392df3693164212
7
- data.tar.gz: 72c752cd43c93434b650b14f46148892d69e17c81fd247ca1bea6dca86e995154c26001aba025fe38bb65b4724eed6ec538ec000d26b18fa2d503cb23f6bf1c2
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 Version](http://img.shields.io/gem/v/ostatus2.svg)][gem]
5
5
  [![Build Status](http://img.shields.io/travis/Gargron/ostatus2.svg)][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
@@ -0,0 +1,3 @@
1
+ module OStatus2
2
+ VERSION = "0.1.1"
3
+ 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.0
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-19 00:00:00.000000000 Z
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.8'
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.8'
69
- description: Toolset for interacting with the OStatus suite of protocols
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 OStatus suite of protocols
105
+ summary: Toolset for interacting with the OStatus2 suite of protocols
101
106
  test_files: []
102
107
  has_rdoc: