ostatus2 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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: