social_stream-ostatus 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/app/assets/images/logos/contact/remote_subject.png +0 -0
- data/app/assets/images/logos/original/remote_subject.png +0 -0
- data/app/assets/images/logos/profile/remote_subject.png +0 -0
- data/app/controllers/pshb_controller.rb +14 -22
- data/app/controllers/remote_subjects_controller.rb +19 -0
- data/app/controllers/salmon_controller.rb +15 -0
- data/app/controllers/webfinger_controller.rb +12 -3
- data/app/decorators/social_stream/base/activity_decorator.rb +3 -0
- data/app/decorators/social_stream/base/actor_decorator.rb +3 -0
- data/app/decorators/social_stream/base/audience_decorator.rb +3 -0
- data/app/decorators/social_stream/base/relation/custom_decorator.rb +3 -0
- data/app/decorators/social_stream/base/tie_decorator.rb +3 -0
- data/app/models/actor_key.rb +30 -0
- data/app/models/remote_subject.rb +111 -15
- data/app/views/remote_subjects/_show.html.erb +6 -0
- data/app/views/remote_subjects/show.html.erb +5 -0
- data/config/routes.rb +7 -4
- data/db/migrate/20120905145030_create_social_stream_ostatus.rb +2 -1
- data/db/migrate/20120918194708_create_actor_keys.rb +18 -0
- data/lib/generators/social_stream/ostatus/install_generator.rb +0 -6
- data/lib/generators/social_stream/ostatus/templates/initializer.rb +16 -2
- data/lib/social_stream/ostatus/activity_streams.rb +79 -0
- data/lib/social_stream/ostatus/controllers/debug_requests.rb +24 -0
- data/lib/social_stream/ostatus/engine.rb +6 -6
- data/lib/social_stream/ostatus/models/activity.rb +41 -0
- data/lib/social_stream/ostatus/models/actor.rb +60 -14
- data/lib/social_stream/ostatus/models/audience.rb +2 -2
- data/lib/social_stream/ostatus/models/object.rb +28 -0
- data/lib/social_stream/ostatus/models/relation/custom.rb +22 -0
- data/lib/social_stream/ostatus/models/tie.rb +24 -0
- data/lib/social_stream/ostatus/version.rb +1 -1
- data/lib/social_stream-ostatus.rb +31 -4
- data/social_stream-ostatus.gemspec +2 -2
- data/spec/controllers/host_meta_controller_spec.rb +10 -0
- data/spec/controllers/remote_subjects_controller_spec.rb +37 -0
- data/spec/controllers/webfinger_controller_spec.rb +13 -0
- data/spec/factories/remote_subject.rb +39 -0
- data/spec/models/actor_key_spec.rb +9 -0
- data/spec/models/post_spec.rb +25 -0
- data/spec/models/remote_subject_spec.rb +24 -0
- data/spec/social_stream_ostatus_activity_streams.spec.rb +31 -0
- metadata +34 -8
- data/app/controllers/remoteusers_controller.rb +0 -30
- data/app/views/remoteusers/index.html.erb +0 -8
Binary file
|
Binary file
|
Binary file
|
@@ -1,34 +1,26 @@
|
|
1
1
|
class PshbController < ApplicationController
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
2
|
+
include SocialStream::Ostatus::Controllers::DebugRequests
|
3
|
+
|
4
|
+
skip_before_filter :verify_authenticity_token
|
5
|
+
|
6
|
+
def index
|
7
|
+
case params['hub.mode']
|
8
|
+
#TODO check PuSH specification about subscribe or async
|
9
|
+
when 'subscribe', 'async'
|
6
10
|
render :text => params['hub.challenge'], :status => 200
|
7
|
-
#
|
11
|
+
# TODO: confirm that params['hub.topic'] is a real
|
8
12
|
# requested subscription by someone in this node
|
9
13
|
return
|
10
|
-
|
11
|
-
|
12
|
-
#sync unsubscription verification
|
13
|
-
if params['hub.mode']=='unsubscribe'
|
14
|
+
when 'unsubscribe'
|
14
15
|
render :text => params['hub.challenge'], :status => 200
|
15
|
-
#
|
16
|
+
# TODO: confirm that params['hub.topic'] is a real
|
16
17
|
# requested unsubscription by someone in this node
|
17
18
|
# and delete permissions/remote actor if necessary
|
18
19
|
return
|
19
20
|
end
|
20
21
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
webfinger_id = origin[5]+"@"+origin[2]
|
25
|
-
|
26
|
-
activity_texts = doc.xpath("//xmlns:content")
|
27
|
-
activity_texts.each do |activity_text|
|
28
|
-
r_user = RemoteSubject.find_by_webfinger_id(webfinger_id)
|
29
|
-
if r_user != nil
|
30
|
-
Post.create!(:text => activity_text.content, :_activity_tie_id => r_user.public_tie)
|
31
|
-
end
|
32
|
-
end
|
22
|
+
SocialStream::ActivityStreams.from_pshb_callback(request.body.read)
|
23
|
+
|
24
|
+
render text: "Success!"
|
33
25
|
end
|
34
26
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class RemoteSubjectsController < ApplicationController
|
2
|
+
def index
|
3
|
+
raise ActiveRecord::NotFound if params[:q].blank?
|
4
|
+
|
5
|
+
@remote_subject =
|
6
|
+
RemoteSubject.find_or_create_by_webfinger_uri!(params[:q])
|
7
|
+
|
8
|
+
redirect_to @remote_subject
|
9
|
+
end
|
10
|
+
|
11
|
+
def show
|
12
|
+
@remote_subject =
|
13
|
+
RemoteSubject.find_by_slug!(params[:id])
|
14
|
+
|
15
|
+
if params[:refresh]
|
16
|
+
@remote_subject.refresh_webfinger!
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class SalmonController < ApplicationController
|
2
|
+
include SocialStream::Ostatus::Controllers::DebugRequests
|
3
|
+
|
4
|
+
skip_before_filter :verify_authenticity_token
|
5
|
+
|
6
|
+
def index
|
7
|
+
actor = Actor.find_by_slug! params[:slug]
|
8
|
+
|
9
|
+
SocialStream::ActivityStreams.from_salmon_callback request.body.read, actor
|
10
|
+
|
11
|
+
# TODO handle errors
|
12
|
+
|
13
|
+
render text: "Success!"
|
14
|
+
end
|
15
|
+
end
|
@@ -5,9 +5,18 @@ class WebfingerController < ActionController::Metal
|
|
5
5
|
def index
|
6
6
|
actor = Actor.find_by_webfinger!(params[:q])
|
7
7
|
|
8
|
-
finger = Proudhon::Finger.new
|
9
|
-
:
|
10
|
-
|
8
|
+
finger = Proudhon::Finger.new(
|
9
|
+
:subject => actor.webfinger_uri,
|
10
|
+
:alias => [polymorphic_url(actor.subject)],
|
11
|
+
:links => {
|
12
|
+
avatar: root_url + actor.logo.url(:original),
|
13
|
+
profile: polymorphic_url([actor.subject, :profile]),
|
14
|
+
updates_from: polymorphic_url([actor.subject, :activities], :format => :atom),
|
15
|
+
salmon: salmon_url(actor.slug),
|
16
|
+
replies: salmon_url(actor.slug),
|
17
|
+
mention: salmon_url(actor.slug),
|
18
|
+
magic_key: actor.magic_public_key
|
19
|
+
})
|
11
20
|
|
12
21
|
self.response_body = finger.to_xml
|
13
22
|
self.content_type = Mime::XML
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
|
3
|
+
# Store OStatus private and public key
|
4
|
+
class ActorKey < ActiveRecord::Base
|
5
|
+
KEY_SIZE = 1024
|
6
|
+
|
7
|
+
belongs_to :actor
|
8
|
+
|
9
|
+
validates_presence_of :key_der
|
10
|
+
|
11
|
+
before_validation :generate_key, on: :create
|
12
|
+
|
13
|
+
def key
|
14
|
+
@key ||=
|
15
|
+
OpenSSL::PKey::RSA.new(key_der)
|
16
|
+
end
|
17
|
+
|
18
|
+
def key= new_key
|
19
|
+
@key = new_key
|
20
|
+
self.key_der = new_key.to_der
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def generate_key
|
26
|
+
return if key_der.present?
|
27
|
+
|
28
|
+
self.key = OpenSSL::PKey::RSA.generate(KEY_SIZE)
|
29
|
+
end
|
30
|
+
end
|
@@ -1,27 +1,53 @@
|
|
1
1
|
class RemoteSubject < ActiveRecord::Base
|
2
|
-
|
2
|
+
include SocialStream::Models::Subject
|
3
|
+
# Create absolute routes
|
4
|
+
include Rails.application.routes.url_helpers
|
5
|
+
|
6
|
+
attr_reader :url_helper
|
7
|
+
attr_accessible :webfinger_id
|
8
|
+
|
9
|
+
# Save webfinger_info hash into the database
|
10
|
+
serialize :webfinger_info
|
11
|
+
|
12
|
+
validates_uniqueness_of :webfinger_id
|
13
|
+
|
14
|
+
before_validation :fill_information,
|
15
|
+
:on => :create
|
16
|
+
|
17
|
+
after_create :subscribe_to_public_feed
|
18
|
+
after_destroy :unsubscribe_to_public_feed
|
19
|
+
|
20
|
+
scope :webfinger_alias, lambda { |uri|
|
21
|
+
where('webfinger_info LIKE ?', "%aliases%#{ uri }%")
|
22
|
+
}
|
3
23
|
|
4
24
|
#validates_format_of :webfinger_slug, :with => Devise.email_regexp, :allow_blank => true
|
5
25
|
|
6
26
|
class << self
|
7
|
-
def
|
8
|
-
|
27
|
+
def find_or_create_by_webfinger_uri!(uri)
|
28
|
+
if uri =~ /^https?:\/\//
|
29
|
+
records = webfinger_alias(uri)
|
9
30
|
|
10
|
-
|
31
|
+
# SQL scope is not reliable
|
32
|
+
if records.present?
|
33
|
+
return records.find{ |r| r.webfinger_aliases.include?(uri) }
|
34
|
+
end
|
11
35
|
|
12
|
-
|
13
|
-
|
36
|
+
# TODO: create by http uri?
|
37
|
+
|
38
|
+
raise ::ActiveRecord::RecordNotFound
|
39
|
+
end
|
40
|
+
|
41
|
+
id = uri.dup
|
42
|
+
|
43
|
+
if id =~ /^acct:/
|
44
|
+
id.gsub!('acct:', '')
|
45
|
+
end
|
46
|
+
|
47
|
+
find_or_create_by_webfinger_id!(id)
|
14
48
|
end
|
15
49
|
end
|
16
50
|
|
17
|
-
# Public feed url for this RemoteSubject
|
18
|
-
#
|
19
|
-
# TODO: get from webfinger?
|
20
|
-
# It does not work for every remote user!
|
21
|
-
def public_feed_url
|
22
|
-
"http://#{ webfinger_url }/api/user/#{ name }/public.atom"
|
23
|
-
end
|
24
|
-
|
25
51
|
# Return the slug in the webfinger_id
|
26
52
|
def webfinger_slug
|
27
53
|
splitted_webfinger_id.first
|
@@ -32,12 +58,82 @@ class RemoteSubject < ActiveRecord::Base
|
|
32
58
|
splitted_webfinger_id.last
|
33
59
|
end
|
34
60
|
|
35
|
-
|
61
|
+
# URL of the activity feed from this {RemoteSubject}
|
62
|
+
def public_feed_url
|
63
|
+
webfinger_info[:updates_from]
|
64
|
+
end
|
65
|
+
|
66
|
+
# URL of the Salmon endpoint for this {RemoteSubject}
|
67
|
+
def salmon_url
|
68
|
+
webfinger_info[:salmon]
|
69
|
+
end
|
70
|
+
|
71
|
+
# Webfinger Alias
|
72
|
+
def webfinger_aliases
|
73
|
+
webfinger_info[:aliases]
|
74
|
+
end
|
75
|
+
|
76
|
+
# Fetch the webfinger again
|
77
|
+
def refresh_webfinger!
|
78
|
+
fill_webfinger_info
|
79
|
+
|
80
|
+
save!
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
36
84
|
|
37
85
|
def splitted_webfinger_id
|
38
86
|
@splitted_webfinger_id ||=
|
39
87
|
webfinger_id.split('@')
|
40
88
|
end
|
41
89
|
|
90
|
+
def fill_information
|
91
|
+
fill_webfinger_info
|
92
|
+
|
93
|
+
self.name = webfinger_id
|
94
|
+
end
|
95
|
+
|
96
|
+
def fill_webfinger_info
|
97
|
+
self.webfinger_info = build_webfinger_info
|
98
|
+
self.rsa_key = finger.magic_key
|
99
|
+
end
|
100
|
+
|
101
|
+
def build_webfinger_info
|
102
|
+
{
|
103
|
+
updates_from: finger.links[:updates_from],
|
104
|
+
salmon: finger.links[:salmon],
|
105
|
+
aliases: finger.alias
|
106
|
+
}
|
107
|
+
end
|
108
|
+
|
109
|
+
def finger
|
110
|
+
@finger ||=
|
111
|
+
fetch_finger
|
112
|
+
end
|
113
|
+
|
114
|
+
def fetch_finger
|
115
|
+
finger =
|
116
|
+
Proudhon::Finger.fetch webfinger_id
|
42
117
|
|
118
|
+
# FIXME custom error
|
119
|
+
raise ::ActiveRecord::RecordNotFound if finger.blank?
|
120
|
+
|
121
|
+
finger
|
122
|
+
end
|
123
|
+
|
124
|
+
def subscribe_to_public_feed
|
125
|
+
return if public_feed_url.blank?
|
126
|
+
|
127
|
+
atom = Proudhon::Atom.from_uri(public_feed_url)
|
128
|
+
|
129
|
+
atom.subscribe(pshb_url(:host => SocialStream::Ostatus.pshb_host))
|
130
|
+
end
|
131
|
+
|
132
|
+
def unsubscribe_to_public_feed
|
133
|
+
return if public_feed_url.blank?
|
134
|
+
|
135
|
+
atom = Proudhon::Atom.from_uri(public_feed_url)
|
136
|
+
|
137
|
+
atom.unsubscribe(pshb_url(:host => SocialStream::Ostatus.pshb_host))
|
138
|
+
end
|
43
139
|
end
|
data/config/routes.rb
CHANGED
@@ -1,10 +1,13 @@
|
|
1
1
|
Rails.application.routes.draw do
|
2
|
-
#
|
2
|
+
# Host Meta
|
3
3
|
match '/.well-known/host-meta', :to => HostMetaController.action(:index)
|
4
4
|
|
5
|
-
#
|
5
|
+
# Webfinger
|
6
6
|
match '/webfinger' => 'webfinger#index', :as => 'webfinger'
|
7
7
|
|
8
|
-
|
9
|
-
match '
|
8
|
+
# PushSubHubBub callback
|
9
|
+
match 'pshb' => 'pshb#index', as: :pshb
|
10
|
+
|
11
|
+
# Salmon callback
|
12
|
+
match 'salmon/:slug' => 'salmon#index', as: :salmon
|
10
13
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class CreateActorKeys < ActiveRecord::Migration
|
2
|
+
def up
|
3
|
+
create_table :actor_keys do |t|
|
4
|
+
t.integer :actor_id
|
5
|
+
t.binary :key_der
|
6
|
+
|
7
|
+
t.timestamps
|
8
|
+
end
|
9
|
+
|
10
|
+
add_index "actor_keys", "actor_id"
|
11
|
+
add_foreign_key "actor_keys", "actors", :name => "actor_keys_on_actor_id"
|
12
|
+
end
|
13
|
+
|
14
|
+
def down
|
15
|
+
remove_foreign_key "actor_keys", :name => "actor_keys_on_actor_id"
|
16
|
+
drop_table :actor_keys
|
17
|
+
end
|
18
|
+
end
|
@@ -13,10 +13,4 @@ class SocialStream::Ostatus::InstallGenerator < Rails::Generators::Base
|
|
13
13
|
def config_initializer
|
14
14
|
copy_file 'initializer.rb', 'config/initializers/social_stream-ostatus.rb'
|
15
15
|
end
|
16
|
-
|
17
|
-
def inject_remote_user_relation
|
18
|
-
append_file 'config/relations.yml',
|
19
|
-
"\nremote_subject:\n friend:\n name: friend\n permissions:\n - [ follow ]\n sphere: personal\n"+
|
20
|
-
" public:\n name: public\n permissions:\n - [ read, tie, star_tie ]\n sphere: personal"
|
21
|
-
end
|
22
16
|
end
|
@@ -1,4 +1,18 @@
|
|
1
1
|
SocialStream::Ostatus.setup do |config|
|
2
|
-
|
3
|
-
|
2
|
+
# Default to the PuSH reference Hub server
|
3
|
+
#
|
4
|
+
# config.hub = 'http://pubsubhubbub.appspot.com'
|
5
|
+
|
6
|
+
# The host where the hub should take the activity feed from
|
7
|
+
#
|
8
|
+
# Local subjects will publish their public activities there
|
9
|
+
config.activity_feed_host = 'localhost:3000'
|
10
|
+
|
11
|
+
# The host where the PuSH should send the callbacks to
|
12
|
+
#
|
13
|
+
# Remote subjects get their local activities updates with the PuSH callback
|
14
|
+
config.pshb_host = 'localhost:3000'
|
15
|
+
|
16
|
+
# Debug OStatus requests
|
17
|
+
# config.debug_requests = true
|
4
18
|
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module SocialStream
|
2
|
+
module Ostatus
|
3
|
+
module ActivityStreams
|
4
|
+
# Parses the body from a {PshbController#index} and dispatches
|
5
|
+
# entries for parsing to {#record_from_entry!}
|
6
|
+
def from_pshb_callback(body)
|
7
|
+
atom = Proudhon::Atom.parse body
|
8
|
+
|
9
|
+
atom.entries.each do |entry|
|
10
|
+
# FIXME: get author from feed
|
11
|
+
# https://github.com/shf/proudhon/issues/8
|
12
|
+
entry.author.uri ||= feed.author.uri
|
13
|
+
|
14
|
+
activity_from_entry! entry
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# Parses an activity form a PuSH or Salmon notification
|
19
|
+
# Decides what action should be taken from an ActivityStreams entry
|
20
|
+
def activity_from_entry! entry, receiver = nil
|
21
|
+
# FIXME: should not use to_sym
|
22
|
+
# https://github.com/shf/proudhon/issues/7
|
23
|
+
case entry.verb.to_sym
|
24
|
+
when :follow
|
25
|
+
Tie.from_entry! entry, receiver
|
26
|
+
else
|
27
|
+
# :post is the default verb
|
28
|
+
r = record_from_entry! entry, receiver
|
29
|
+
r.post_activity
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Redirects parsing to the suitable SocialStream's model
|
34
|
+
def record_from_entry! entry, receiver
|
35
|
+
model!(entry.objtype).from_entry! entry, receiver
|
36
|
+
end
|
37
|
+
|
38
|
+
# Finds or creates a {RemoteSubject} from an ActivityStreams entry
|
39
|
+
#
|
40
|
+
def actor_from_entry! entry
|
41
|
+
webfinger_id = entry.author.uri
|
42
|
+
|
43
|
+
if webfinger_id.blank?
|
44
|
+
raise "Entry author without uri: #{ entry.to_xml }"
|
45
|
+
end
|
46
|
+
|
47
|
+
RemoteSubject.find_or_create_by_webfinger_uri! webfinger_id
|
48
|
+
end
|
49
|
+
|
50
|
+
# Parses the body from a {Salmon#index} and receiving actor
|
51
|
+
def from_salmon_callback(body, receiver)
|
52
|
+
salmon = Proudhon::Salmon.new body
|
53
|
+
|
54
|
+
validate_salmon salmon
|
55
|
+
|
56
|
+
activity_from_entry! salmon.to_entry, receiver
|
57
|
+
end
|
58
|
+
|
59
|
+
def validate_salmon salmon
|
60
|
+
remote_subject = RemoteSubject.find_or_create_by_webfinger_uri!(salmon.to_entry.author.uri)
|
61
|
+
key = remote_subject.rsa_key
|
62
|
+
|
63
|
+
unless salmon.verify(key)
|
64
|
+
raise "Invalid salmon: #{ salmon }"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Translate SocialStream activity verb to Proudhon verb
|
69
|
+
def verb orig
|
70
|
+
case orig
|
71
|
+
when 'make-friend'
|
72
|
+
:follow
|
73
|
+
else
|
74
|
+
orig.to_sym
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module SocialStream
|
2
|
+
module Ostatus
|
3
|
+
module Controllers
|
4
|
+
module DebugRequests
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
before_filter :debug_request
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def debug_request
|
14
|
+
return unless SocialStream::Ostatus.debug_requests
|
15
|
+
|
16
|
+
logger.info request.body.read
|
17
|
+
|
18
|
+
# Set StringIO to initial state for the action to get the content
|
19
|
+
request.body.rewind
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -1,15 +1,15 @@
|
|
1
1
|
module SocialStream
|
2
2
|
module Ostatus
|
3
3
|
class Engine < Rails::Engine
|
4
|
-
initializer 'social_stream-ostatus.
|
5
|
-
|
6
|
-
|
4
|
+
initializer 'social_stream-ostatus.activity_streams' do
|
5
|
+
SocialStream::ActivityStreams.class_eval do
|
6
|
+
extend SocialStream::Ostatus::ActivityStreams
|
7
7
|
end
|
8
8
|
end
|
9
9
|
|
10
|
-
initializer 'social_stream-ostatus.
|
11
|
-
|
12
|
-
include SocialStream::Ostatus::Models::
|
10
|
+
initializer 'social_stream-ostatus.models.object' do
|
11
|
+
SocialStream::Models::Object::ClassMethods.module_eval do
|
12
|
+
include SocialStream::Ostatus::Models::Object::ClassMethods
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module SocialStream
|
2
|
+
module Ostatus
|
3
|
+
module Models
|
4
|
+
module Activity
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
after_commit :send_salmon
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
# Send Salmon notification to remote subject
|
14
|
+
def send_salmon
|
15
|
+
return if sender.subject_type == "RemoteSubject" ||
|
16
|
+
receiver.subject_type != "RemoteSubject"
|
17
|
+
|
18
|
+
entry =
|
19
|
+
Proudhon::Entry.new id: "tag:#{ SocialStream::Ostatus.activity_feed_host },2005:activity-#{ id }",
|
20
|
+
title: stream_title,
|
21
|
+
content: stream_content,
|
22
|
+
verb: SocialStream::ActivityStreams.verb(verb),
|
23
|
+
author: Proudhon::Author.new(name: sender.name,
|
24
|
+
uri: sender.webfinger_uri)
|
25
|
+
salmon = entry.to_salmon
|
26
|
+
|
27
|
+
if SocialStream::Ostatus.debug_requests
|
28
|
+
logger.info entry.to_xml
|
29
|
+
end
|
30
|
+
|
31
|
+
# FIXME: Rails 4 queues
|
32
|
+
Thread.new do
|
33
|
+
salmon.deliver receiver_subject.salmon_url, sender.rsa_key
|
34
|
+
|
35
|
+
ActiveRecord::Base.connection.close
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -6,9 +6,15 @@ module SocialStream
|
|
6
6
|
module Models
|
7
7
|
module Actor
|
8
8
|
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
include Rails.application.routes.url_helpers
|
9
11
|
|
10
12
|
included do
|
11
|
-
|
13
|
+
has_one :actor_key, dependent: :destroy,
|
14
|
+
validate: true,
|
15
|
+
autosave: true
|
16
|
+
|
17
|
+
after_commit :publish_feed
|
12
18
|
end
|
13
19
|
|
14
20
|
module ClassMethods
|
@@ -20,21 +26,61 @@ module SocialStream
|
|
20
26
|
find_by_slug! $2
|
21
27
|
end
|
22
28
|
end
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
#
|
29
|
+
|
30
|
+
# The Webfinger ID for this {Actor}
|
31
|
+
def webfinger_id
|
32
|
+
"#{ slug }@#{ SocialStream::Ostatus.activity_feed_host }"
|
27
33
|
end
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
34
|
+
|
35
|
+
# The Webfinger URI for this {Actor}
|
36
|
+
def webfinger_uri
|
37
|
+
"acct:#{ webfinger_id }"
|
38
|
+
end
|
39
|
+
|
40
|
+
# Fetch or create the associated {ActorKey}
|
41
|
+
def actor_key!
|
42
|
+
actor_key ||
|
43
|
+
create_actor_key!
|
44
|
+
end
|
45
|
+
|
46
|
+
# OpenSSL::PKey::RSA key
|
47
|
+
#
|
48
|
+
# The key is generated if it does not exist
|
49
|
+
def rsa_key
|
50
|
+
actor_key!.key
|
51
|
+
end
|
52
|
+
|
53
|
+
# Set OpenSSL::PKey::RSA key
|
54
|
+
def rsa_key= key
|
55
|
+
k = actor_key || build_actor_key
|
56
|
+
k.key = key
|
57
|
+
end
|
58
|
+
|
59
|
+
# Public RSA instance of {#rsa_key}
|
60
|
+
def rsa_public_key
|
61
|
+
rsa_key.public_key
|
62
|
+
end
|
63
|
+
|
64
|
+
# MagicKey string from public key
|
65
|
+
def magic_public_key
|
66
|
+
Proudhon::MagicKey.to_s rsa_public_key
|
67
|
+
end
|
68
|
+
|
69
|
+
def publish_feed
|
70
|
+
return if subject_type == "RemoteSubject"
|
71
|
+
|
72
|
+
# FIXME: Rails 4 queues
|
73
|
+
Thread.new do
|
74
|
+
uri = URI.parse(SocialStream::Ostatus.hub)
|
75
|
+
topic = polymorphic_url [subject, :activities],
|
76
|
+
:format => :atom,
|
77
|
+
:host => SocialStream::Ostatus.activity_feed_host
|
33
78
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
79
|
+
response = Net::HTTP::post_form uri, { 'hub.mode' => 'publish',
|
80
|
+
'hub.url' => topic }
|
81
|
+
#TODO: process 4XX look at: response.status
|
82
|
+
|
83
|
+
ActiveRecord::Base.connection.close
|
38
84
|
end
|
39
85
|
end
|
40
86
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module SocialStream
|
2
|
+
module Ostatus
|
3
|
+
module Models
|
4
|
+
module Object
|
5
|
+
module ClassMethods
|
6
|
+
# Creates an new instance from ActivityStreams entry
|
7
|
+
#
|
8
|
+
def from_entry! entry, receiver
|
9
|
+
create! do |obj|
|
10
|
+
obj.author =
|
11
|
+
obj.user_author =
|
12
|
+
SocialStream::ActivityStreams.actor_from_entry!(entry)
|
13
|
+
|
14
|
+
obj.owner = receiver || obj.author
|
15
|
+
|
16
|
+
obj.title = entry.title
|
17
|
+
obj.description = entry.summary || entry.content
|
18
|
+
|
19
|
+
obj.relation_ids = [ ::Relation::Public.instance.id ]
|
20
|
+
|
21
|
+
yield obj if block_given?
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module SocialStream
|
2
|
+
module Ostatus
|
3
|
+
module Models
|
4
|
+
module Relation
|
5
|
+
module Custom
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do
|
9
|
+
const_get("DEFAULT")['remote_subject'] = {
|
10
|
+
'default' => {
|
11
|
+
'name' => 'default',
|
12
|
+
'permissions' => [
|
13
|
+
[ 'read', 'activity' ]
|
14
|
+
]
|
15
|
+
}
|
16
|
+
}
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module SocialStream
|
2
|
+
module Ostatus
|
3
|
+
module Models
|
4
|
+
module Tie
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
# Create a new {Tie} from OStatus entry
|
9
|
+
def from_entry! entry, receiver
|
10
|
+
# Sender must be remote
|
11
|
+
sender = RemoteSubject.find_or_create_by_webfinger_uri! entry.author.uri
|
12
|
+
|
13
|
+
contact = sender.contact_to!(receiver)
|
14
|
+
|
15
|
+
# FIXME: hack
|
16
|
+
contact.user_author = sender
|
17
|
+
|
18
|
+
contact.relation_ids = [::Relation::Public.instance.id]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -5,21 +5,48 @@ require 'proudhon'
|
|
5
5
|
|
6
6
|
module SocialStream
|
7
7
|
module Ostatus
|
8
|
+
# PuSH hub
|
8
9
|
mattr_accessor :hub
|
9
|
-
|
10
|
-
|
11
|
-
mattr_accessor :node_base_url
|
12
|
-
@@node_base_url = :node_base_url
|
10
|
+
# Default to the PubSubHubbub reference Hub server
|
11
|
+
@@hub = 'http://pubsubhubbub.appspot.com'
|
13
12
|
|
13
|
+
# The host where the hub should take the activity feed from
|
14
|
+
mattr_accessor :activity_feed_host
|
15
|
+
@@activity_feed_host = 'localhost:3000'
|
16
|
+
|
17
|
+
# The host where the PuSH should send the callbacks to
|
18
|
+
mattr_accessor :pshb_host
|
19
|
+
@@pshb_host = 'localhost:3000'
|
20
|
+
|
21
|
+
# Debug OStatus request with logger.info
|
22
|
+
mattr_accessor :debug_requests
|
23
|
+
@@debug_requests = false
|
24
|
+
|
14
25
|
class << self
|
15
26
|
def setup
|
16
27
|
yield self
|
17
28
|
end
|
18
29
|
end
|
19
30
|
|
31
|
+
autoload :ActivityStreams, 'social_stream/ostatus/activity_streams'
|
32
|
+
|
20
33
|
module Models
|
34
|
+
autoload :Activity, 'social_stream/ostatus/models/activity'
|
21
35
|
autoload :Actor, 'social_stream/ostatus/models/actor'
|
22
36
|
autoload :Audience, 'social_stream/ostatus/models/audience'
|
37
|
+
autoload :Tie, 'social_stream/ostatus/models/tie'
|
38
|
+
|
39
|
+
module Object
|
40
|
+
autoload :ClassMethods, 'social_stream/ostatus/models/object'
|
41
|
+
end
|
42
|
+
|
43
|
+
module Relation
|
44
|
+
autoload :Custom, 'social_stream/ostatus/models/relation/custom'
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
module Controllers
|
49
|
+
autoload :DebugRequests, 'social_stream/ostatus/controllers/debug_requests'
|
23
50
|
end
|
24
51
|
end
|
25
52
|
end
|
@@ -12,8 +12,8 @@ Gem::Specification.new do |s|
|
|
12
12
|
s.files = `git ls-files`.split("\n")
|
13
13
|
|
14
14
|
# Gem dependencies
|
15
|
-
s.add_runtime_dependency('social_stream-base','~> 0.
|
16
|
-
s.add_runtime_dependency('proudhon','>= 0.3')
|
15
|
+
s.add_runtime_dependency('social_stream-base', '~> 0.23.0')
|
16
|
+
s.add_runtime_dependency('proudhon','>= 0.3.5')
|
17
17
|
s.add_runtime_dependency('nokogiri','> 1.4.4')
|
18
18
|
|
19
19
|
# Development Gem dependencies
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe RemoteSubjectsController do
|
4
|
+
render_views
|
5
|
+
|
6
|
+
context "with remote subject" do
|
7
|
+
before do
|
8
|
+
@remote_subject = Factory(:remote_subject)
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should redirect index to show" do
|
12
|
+
get :index, q: @remote_subject.webfinger_id
|
13
|
+
|
14
|
+
response.should redirect_to(@remote_subject)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should render show" do
|
18
|
+
get :show, id: @remote_subject.slug
|
19
|
+
|
20
|
+
response.should be_success
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
describe "refreshing show" do
|
25
|
+
before do
|
26
|
+
RemoteSubject.should_receive(:find_by_slug!).with(@remote_subject.slug) { @remote_subject }
|
27
|
+
@remote_subject.should_receive(:refresh_webfinger!)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should refresh remote_subject" do
|
31
|
+
get :show, id: @remote_subject.slug, refresh: true
|
32
|
+
|
33
|
+
response.should be_success
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
class << Proudhon::Finger
|
2
|
+
def fetch id
|
3
|
+
obj = Object.new
|
4
|
+
def obj.links
|
5
|
+
{ updates_from: 'feed' }
|
6
|
+
end
|
7
|
+
|
8
|
+
def obj.magic_key
|
9
|
+
OpenSSL::PKey::RSA.generate 256
|
10
|
+
end
|
11
|
+
|
12
|
+
obj
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class << Proudhon::Atom
|
17
|
+
def from_uri uri
|
18
|
+
obj = Object.new
|
19
|
+
|
20
|
+
def obj.subscribe(callback)
|
21
|
+
true
|
22
|
+
end
|
23
|
+
|
24
|
+
obj
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
RemoteSubject.class_eval do
|
29
|
+
def build_webfinger_info
|
30
|
+
{
|
31
|
+
aliases: [ "http://example.com/#{ webfinger_id.split('@').first }" ]
|
32
|
+
}
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
Factory.define :remote_subject do |s|
|
38
|
+
s.sequence(:webfinger_id) { |n| "remote_subject-#{ n }@example.com" }
|
39
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Post do
|
4
|
+
describe "from_entry!" do
|
5
|
+
before do
|
6
|
+
@remote_subject = Factory(:remote_subject)
|
7
|
+
@entry = double("Proudhon::Entry")
|
8
|
+
|
9
|
+
SocialStream::ActivityStreams.should_receive(:actor_from_entry!) { @remote_subject }
|
10
|
+
|
11
|
+
@entry.should_receive(:title) { "testing" }
|
12
|
+
@entry.should_receive(:summary) { "testing" }
|
13
|
+
|
14
|
+
end
|
15
|
+
it "should create post" do
|
16
|
+
post_count = Post.count
|
17
|
+
|
18
|
+
post = Post.from_entry! @entry, nil
|
19
|
+
|
20
|
+
post.author.should == @remote_subject.actor
|
21
|
+
|
22
|
+
Post.count.should == post_count + 1
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe RemoteSubject do
|
4
|
+
describe "find_or_create_by_webfinger_uri" do
|
5
|
+
before do
|
6
|
+
@remote_subject = Factory(:remote_subject)
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should find without acct:" do
|
10
|
+
RemoteSubject.find_or_create_by_webfinger_uri!(@remote_subject.webfinger_id).should == @remote_subject
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should find with acct:" do
|
14
|
+
RemoteSubject.find_or_create_by_webfinger_uri!("acct:#{ @remote_subject.webfinger_id}").should == @remote_subject
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should find with alias" do
|
18
|
+
splt = @remote_subject.webfinger_id.split('@')
|
19
|
+
uri = "http://#{ splt.last }/#{ splt.first }"
|
20
|
+
|
21
|
+
RemoteSubject.find_or_create_by_webfinger_uri!(uri)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe SocialStream::Ostatus::ActivityStreams do
|
4
|
+
describe "from_pshb_callback" do
|
5
|
+
before do
|
6
|
+
@entry = double("Proudhon::Entry")
|
7
|
+
|
8
|
+
@remote_subject = double("RemoteSubject")
|
9
|
+
|
10
|
+
Proudhon::Atom.should_receive(:parse) { [ @entry ] }
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "with post note" do
|
14
|
+
before do
|
15
|
+
@entry.should_receive(:verb) { :post }
|
16
|
+
@entry.should_receive(:objtype) { :note }
|
17
|
+
|
18
|
+
@post = double "post"
|
19
|
+
|
20
|
+
@post.should_receive :post_activity
|
21
|
+
|
22
|
+
Post.should_receive(:from_entry!).with(@entry) { @post }
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should call stubs" do
|
26
|
+
SocialStream::ActivityStreams.from_pshb_callback "test"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: social_stream-ostatus
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2012-
|
13
|
+
date: 2012-10-03 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: social_stream-base
|
@@ -19,7 +19,7 @@ dependencies:
|
|
19
19
|
requirements:
|
20
20
|
- - ~>
|
21
21
|
- !ruby/object:Gem::Version
|
22
|
-
version: 0.
|
22
|
+
version: 0.23.0
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -27,7 +27,7 @@ dependencies:
|
|
27
27
|
requirements:
|
28
28
|
- - ~>
|
29
29
|
- !ruby/object:Gem::Version
|
30
|
-
version: 0.
|
30
|
+
version: 0.23.0
|
31
31
|
- !ruby/object:Gem::Dependency
|
32
32
|
name: proudhon
|
33
33
|
requirement: !ruby/object:Gem::Requirement
|
@@ -35,7 +35,7 @@ dependencies:
|
|
35
35
|
requirements:
|
36
36
|
- - ! '>='
|
37
37
|
- !ruby/object:Gem::Version
|
38
|
-
version:
|
38
|
+
version: 0.3.5
|
39
39
|
type: :runtime
|
40
40
|
prerelease: false
|
41
41
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -43,7 +43,7 @@ dependencies:
|
|
43
43
|
requirements:
|
44
44
|
- - ! '>='
|
45
45
|
- !ruby/object:Gem::Version
|
46
|
-
version:
|
46
|
+
version: 0.3.5
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: nokogiri
|
49
49
|
requirement: !ruby/object:Gem::Requirement
|
@@ -152,25 +152,46 @@ files:
|
|
152
152
|
- README.rdoc
|
153
153
|
- Rakefile
|
154
154
|
- app/assets/images/logos/actor/remote_subject.png
|
155
|
+
- app/assets/images/logos/contact/remote_subject.png
|
156
|
+
- app/assets/images/logos/original/remote_subject.png
|
157
|
+
- app/assets/images/logos/profile/remote_subject.png
|
155
158
|
- app/controllers/host_meta_controller.rb
|
156
159
|
- app/controllers/pshb_controller.rb
|
157
|
-
- app/controllers/
|
160
|
+
- app/controllers/remote_subjects_controller.rb
|
161
|
+
- app/controllers/salmon_controller.rb
|
158
162
|
- app/controllers/subjects_controller.rb
|
159
163
|
- app/controllers/webfinger_controller.rb
|
164
|
+
- app/decorators/social_stream/base/activity_decorator.rb
|
165
|
+
- app/decorators/social_stream/base/actor_decorator.rb
|
166
|
+
- app/decorators/social_stream/base/audience_decorator.rb
|
167
|
+
- app/decorators/social_stream/base/relation/custom_decorator.rb
|
168
|
+
- app/decorators/social_stream/base/tie_decorator.rb
|
169
|
+
- app/models/actor_key.rb
|
160
170
|
- app/models/remote_subject.rb
|
161
|
-
- app/views/
|
171
|
+
- app/views/remote_subjects/_show.html.erb
|
172
|
+
- app/views/remote_subjects/show.html.erb
|
162
173
|
- config/locales/en.yml
|
163
174
|
- config/routes.rb
|
164
175
|
- db/migrate/20120905145030_create_social_stream_ostatus.rb
|
176
|
+
- db/migrate/20120918194708_create_actor_keys.rb
|
165
177
|
- lib/generators/social_stream/ostatus/install_generator.rb
|
166
178
|
- lib/generators/social_stream/ostatus/templates/initializer.rb
|
167
179
|
- lib/social_stream-ostatus.rb
|
168
180
|
- lib/social_stream/migrations/ostatus.rb
|
181
|
+
- lib/social_stream/ostatus/activity_streams.rb
|
182
|
+
- lib/social_stream/ostatus/controllers/debug_requests.rb
|
169
183
|
- lib/social_stream/ostatus/engine.rb
|
184
|
+
- lib/social_stream/ostatus/models/activity.rb
|
170
185
|
- lib/social_stream/ostatus/models/actor.rb
|
171
186
|
- lib/social_stream/ostatus/models/audience.rb
|
187
|
+
- lib/social_stream/ostatus/models/object.rb
|
188
|
+
- lib/social_stream/ostatus/models/relation/custom.rb
|
189
|
+
- lib/social_stream/ostatus/models/tie.rb
|
172
190
|
- lib/social_stream/ostatus/version.rb
|
173
191
|
- social_stream-ostatus.gemspec
|
192
|
+
- spec/controllers/host_meta_controller_spec.rb
|
193
|
+
- spec/controllers/remote_subjects_controller_spec.rb
|
194
|
+
- spec/controllers/webfinger_controller_spec.rb
|
174
195
|
- spec/dummy/Rakefile
|
175
196
|
- spec/dummy/app/controllers/application_controller.rb
|
176
197
|
- spec/dummy/app/helpers/application_helper.rb
|
@@ -202,8 +223,13 @@ files:
|
|
202
223
|
- spec/dummy/public/javascripts/rails.js
|
203
224
|
- spec/dummy/public/stylesheets/.gitkeep
|
204
225
|
- spec/dummy/script/rails
|
226
|
+
- spec/factories/remote_subject.rb
|
205
227
|
- spec/integration/navigation_spec.rb
|
228
|
+
- spec/models/actor_key_spec.rb
|
229
|
+
- spec/models/post_spec.rb
|
230
|
+
- spec/models/remote_subject_spec.rb
|
206
231
|
- spec/social_stream_ostatus.spec.rb
|
232
|
+
- spec/social_stream_ostatus_activity_streams.spec.rb
|
207
233
|
- spec/spec_helper.rb
|
208
234
|
homepage: http://social-stream.dit.upm.es
|
209
235
|
licenses: []
|
@@ -1,30 +0,0 @@
|
|
1
|
-
class RemoteusersController < ApplicationController
|
2
|
-
before_filter :authenticate_user!
|
3
|
-
|
4
|
-
def index
|
5
|
-
if params[:slug].present?
|
6
|
-
#Selecting the remote subject
|
7
|
-
u = RemoteSubject.find_or_create_using_wslug(params[:slug])
|
8
|
-
|
9
|
-
#Creating the tie between me and the remote subject
|
10
|
-
t = Tie.create!(:sender => current_user.actor,
|
11
|
-
:receiver => u.actor,
|
12
|
-
:relation_name => "friend")
|
13
|
-
|
14
|
-
#Requesting a subscription to the hub
|
15
|
-
t = Thread.new do
|
16
|
-
uri = URI.parse(SocialStream::Ostatus.hub)
|
17
|
-
response = Net::HTTP::post_form(uri,{ 'hub.callback' => pshb_callback_url,
|
18
|
-
'hub.mode' => "subscribe",
|
19
|
-
'hub.topic' => u.public_feed_url,
|
20
|
-
'hub.verify' => 'sync'})
|
21
|
-
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
respond_to do |format|
|
26
|
-
format.html
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
end
|