EM websocket cleanly merged with master. webfingering is now async

This commit is contained in:
maxwell 2010-10-24 18:21:01 -07:00
parent 51dd6d32b3
commit 8fe339b03b
28 changed files with 502 additions and 270 deletions

View file

@ -1,7 +1,7 @@
# Copyright (c) 2010, Diaspora Inc. This file is # Copyright (c) 2010, Diaspora Inc. This file is
# licensed under the Affero General Public License version 3 or later. See # licensed under the Affero General Public License version 3 or later. See
# the COPYRIGHT file. # the COPYRIGHT file.
require File.join(Rails.root, 'lib/em-webfinger')
class DevUtilitiesController < ApplicationController class DevUtilitiesController < ApplicationController
before_filter :authenticate_user!, :except => [:set_backer_number, :log] before_filter :authenticate_user!, :except => [:set_backer_number, :log]
include ApplicationHelper include ApplicationHelper
@ -10,14 +10,25 @@ class DevUtilitiesController < ApplicationController
def zombiefriends def zombiefriends
render :nothing => true render :nothing => true
bkr_info = backer_info bkr_info = backer_info
if current_user.email == "tom@tom.joindiaspora.com" if current_user.email == "tom@tom.joindiaspora.com"
puts bkr_info.inspect
bkr_info.each do |backer| bkr_info.each do |backer|
backer_email = "#{backer['username']}@#{backer['username']}.joindiaspora.com" backer_email = "#{backer['username']}@#{backer['username']}.joindiaspora.com"
rel_hash = relationship_flow(backer_email)
logger.info "Zombiefriending #{backer['given_name']} #{backer['family_name']}" webfinger = EMWebfinger.new(backer_email)
logger.info "Calling send_friend_request with #{rel_hash[:friend]} and #{current_user.aspects.first}"
current_user.send_friend_request_to(rel_hash[:friend], current_user.aspects.first) webfinger.on_person { |person|
puts person.inspect
if person.respond_to? :diaspora_handle
rel_hash = {:friend => person}
logger.info "Zombiefriending #{backer['given_name']} #{backer['family_name']}"
logger.info "Calling send_friend_request with #{rel_hash[:friend]} and #{current_user.aspects.first}"
current_user.send_friend_request_to(rel_hash[:friend], current_user.aspects.first)
else
puts "error: #{person}"
end
}
webfinger.fetch
end end
end end
end end

View file

@ -22,8 +22,8 @@ class PublicsController < ApplicationController
end end
def webfinger def webfinger
@person = Person.by_webfinger(params[:q], :local => true) if params[:q] @person = Person.local_by_account_identifier(params[:q]) if params[:q]
unless @person.nil? || @person.owner.nil? unless @person.nil?
render 'webfinger', :content_type => 'application/xrd+xml' render 'webfinger', :content_type => 'application/xrd+xml'
else else
render :nothing => true, :status => 404 render :nothing => true, :status => 404
@ -33,7 +33,7 @@ class PublicsController < ApplicationController
def hub def hub
if params['hub.mode'] == 'subscribe' || params['hub.mode'] == 'unsubscribe' if params['hub.mode'] == 'subscribe' || params['hub.mode'] == 'unsubscribe'
render :text => params['hub.challenge'], :status => 202, :layout => false render :text => params['hub.challenge'], :status => 202, :layout => false
end end
end end
def receive def receive

View file

@ -2,6 +2,8 @@
# licensed under the Affero General Public License version 3 or later. See # licensed under the Affero General Public License version 3 or later. See
# the COPYRIGHT file. # the COPYRIGHT file.
require File.join(Rails.root, 'lib/em-webfinger')
class RequestsController < ApplicationController class RequestsController < ApplicationController
before_filter :authenticate_user! before_filter :authenticate_user!
include RequestsHelper include RequestsHelper
@ -31,65 +33,39 @@ class RequestsController < ApplicationController
def create def create
aspect = current_user.aspect_by_id(params[:request][:aspect_id]) aspect = current_user.aspect_by_id(params[:request][:aspect_id])
account = params[:request][:destination_url].strip
begin
finger = EMWebfinger.new(account)
finger.on_person{ |person|
if person.class == Person
rel_hash = {:friend => person}
begin Rails.logger.debug("Sending request: #{rel_hash}")
rel_hash = relationship_flow(params[:request][:destination_url].strip)
rescue Exception => e
if e.message.include? "not found"
flash[:error] = I18n.t 'requests.create.error'
elsif e.message.include? "Connection timed out"
flash[:error] = I18n.t 'requests.create.error_server'
elsif e.message == "Identifier is invalid"
flash[:error] = I18n.t 'requests.create.invalid_identity'
else
raise e
end
if params[:getting_started] begin
redirect_to getting_started_path(:step=>params[:getting_started]) @request = current_user.send_friend_request_to(rel_hash[:friend], aspect)
rescue Exception => e
Rails.logger.debug("error: #{e.message}")
flash[:error] = e.message
end
else else
respond_with :location => aspect Rails.logger.info "#{person}"
end end
return }
rescue Exception => e
flash[:error] = e.message
end end
# rel_hash = {:friend => params[:friend_handle]} if params[:getting_started]
Rails.logger.debug("Sending request: #{rel_hash}") redirect_to getting_started_path(:step=>params[:getting_started])
begin
@request = current_user.send_friend_request_to(rel_hash[:friend], aspect)
rescue Exception => e
if e.message.include? "yourself"
flash[:error] = I18n.t 'requests.create.yourself', :destination_url => params[:request][:destination_url]
elsif e.message.include? "already"
flash[:notice] = I18n.t 'requests.create.already_friends', :destination_url => params[:request][:destination_url]
else
raise e
end
if params[:getting_started]
redirect_to getting_started_path(:step=>params[:getting_started])
else
respond_with :location => aspect
end
return
end
if @request
flash[:notice] = I18n.t 'requests.create.success',:destination_url => @request.destination_url
if params[:getting_started]
redirect_to getting_started_path(:step=>params[:getting_started])
else
respond_with :location => aspect
end
else else
flash[:error] = I18n.t 'requests.create.horribly_wrong' flash[:notice] = "we tried our best to send a message to #{account}" unless flash[:error]
if params[:getting_started] respond_with :location => aspects_manage_path
redirect_to getting_started_path(:step=>params[:getting_started]) return
else end
respond_with :location => aspect
end
end
end end
end end

View file

@ -3,38 +3,4 @@
# the COPYRIGHT file. # the COPYRIGHT file.
module RequestsHelper module RequestsHelper
def subscription_mode(profile)
if diaspora?(profile)
:friend
else
:none
end
end
def diaspora?(profile)
profile_contains(profile, 'http://joindiaspora.com/seed_location')
end
def profile_contains(profile, rel)
profile.links.each{|x| return true if x.rel == rel}
false
end
def subscription_url(action, profile)
if action == :friend
profile.links.select{|x| x.rel == 'http://joindiaspora.com/seed_location'}.first.href
else
nil
end
end
def relationship_flow(identifier)
action = :none
person = nil
person = Person.by_webfinger identifier
if person
action = (person == current_user.person ? :none : :friend)
end
{ action => person }
end
end end

View file

@ -23,6 +23,13 @@ class Person
one :profile, :class_name => 'Profile' one :profile, :class_name => 'Profile'
validate :profile_is_valid validate :profile_is_valid
before_save :downcase_diaspora_handle
def downcase_diaspora_handle
diaspora_handle.downcase!
end
def profile_is_valid def profile_is_valid
if profile.present? && !profile.valid? if profile.present? && !profile.valid?
profile.errors.full_messages.each { |m| errors.add(:base, m) } profile.errors.full_messages.each { |m| errors.add(:base, m) }
@ -97,61 +104,35 @@ class Person
@serialized_public_key = new_key @serialized_public_key = new_key
end end
def self.by_webfinger(identifier, opts = {}) #database calls
# Raise an error if identifier has a port number def self.by_account_identifier(identifier)
raise "Identifier is invalid" if(identifier.strip.match(/\:\d+$/)) identifier = identifier.strip.downcase.gsub('acct:', '') if identifier
# Raise an error if identifier is not a valid email (generous regexp) self.first(:diaspora_handle => identifier)
raise "Identifier is invalid" if !(identifier =~ /\A.*\@.*\..*\Z/)
query = /\A^#{Regexp.escape(identifier.gsub('acct:', '').to_s)}\z/i
local_person = Person.first(:diaspora_handle => query)
if local_person
Rails.logger.info("Do not need to webfinger, found a local person #{local_person.real_name}")
local_person
elsif !identifier.include?("localhost") && !opts[:local]
#Get remote profile
begin
Rails.logger.info("Webfingering #{identifier}")
f = Redfinger.finger(identifier)
rescue SocketError => e
raise "Diaspora server for #{identifier} not found" if e.message =~ /Name or service not known/
rescue Errno::ETIMEDOUT => e
raise "Connection timed out to Diaspora server for #{identifier}"
end
raise "No webfinger profile found at #{identifier}" if f.nil? || f.links.empty?
Person.from_webfinger_profile(identifier, f)
end
end end
def self.from_webfinger_profile(identifier, profile) def self.local_by_account_identifier(identifier)
person = self.by_account_identifier(identifier)
(person.nil? || person.remote?) ? nil : person
end
def self.build_from_webfinger(profile, hcard)
return nil if profile.nil? || !profile.valid_diaspora_profile?
new_person = Person.new new_person = Person.new
new_person.exported_key = profile.public_key
new_person.id = profile.guid
new_person.diaspora_handle = profile.account
new_person.url = profile.seed_location
public_key_entry = profile.links.select { |x| x.rel == 'diaspora-public-key' } #hcard_profile = HCard.find profile.hcard.first[:href]
Rails.logger.info("hcard: #{ hcard.inspect}")
return nil unless public_key_entry
pubkey = public_key_entry.first.href
new_person.exported_key = Base64.decode64 pubkey
guid = profile.links.select { |x| x.rel == 'http://joindiaspora.com/guid' }.first.href
new_person.id = guid
new_person.diaspora_handle = identifier
hcard = HCard.find profile.hcard.first[:href]
new_person.url = hcard[:url] new_person.url = hcard[:url]
new_person.profile = Profile.new(:first_name => hcard[:given_name], :last_name => hcard[:family_name], :image_url => hcard[:photo]) new_person.profile = Profile.new(:first_name => hcard[:given_name], :last_name => hcard[:family_name], :image_url => hcard[:photo])
if new_person.save
new_person new_person.save! ? new_person : nil
else
nil
end
end end
def remote? def remote?
owner.nil? owner_id.nil?
end end
def as_json(opts={}) def as_json(opts={})
@ -159,7 +140,7 @@ class Person
:person => { :person => {
:id => self.id, :id => self.id,
:name => self.real_name, :name => self.real_name,
:diaspora_handle => self.diaspora_handle, :diaspora_handle => self.diaspora_handle,
:url => self.url, :url => self.url,
:exported_key => exported_key :exported_key => exported_key
} }

View file

@ -42,7 +42,7 @@ class User
key :getting_started, Boolean, :default => true key :getting_started, Boolean, :default => true
before_validation :strip_username, :on => :create before_validation :strip_and_downcase_username, :on => :create
validates_presence_of :username validates_presence_of :username
validates_uniqueness_of :username, :case_sensitive => false validates_uniqueness_of :username, :case_sensitive => false
validates_format_of :username, :with => /\A[A-Za-z0-9_.]+\z/ validates_format_of :username, :with => /\A[A-Za-z0-9_.]+\z/
@ -69,9 +69,10 @@ class User
before_destroy :unfriend_everyone, :remove_person before_destroy :unfriend_everyone, :remove_person
def strip_username def strip_and_downcase_username
if username.present? if username.present?
username.strip! username.strip!
username.downcase!
end end
end end

View file

@ -10,13 +10,13 @@
</dd> </dd>
</dl> </dl>
<dl class="entity_given_name"> <dl class="entity_given_name">
<dt>Full name</dt> <dt>First name</dt>
<dd> <dd>
<span class="given_name" ><%= @person.profile.first_name %></span> <span class="given_name" ><%= @person.profile.first_name %></span>
</dd> </dd>
</dl> </dl>
<dl class="entity_family_name"> <dl class="entity_family_name">
<dt>Full name</dt> <dt>Family name</dt>
<dd> <dd>
<span class="family_name" ><%= @person.profile.last_name %></span> <span class="family_name" ><%= @person.profile.last_name %></span>
</dd> </dd>

View file

@ -134,6 +134,7 @@ namespace :db do
task :tom_seed, :roles => :tom do task :tom_seed, :roles => :tom do
run "cd #{current_path} && bundle exec rake db:seed:tom --trace RAILS_ENV=#{rails_env}" run "cd #{current_path} && bundle exec rake db:seed:tom --trace RAILS_ENV=#{rails_env}"
run "curl -silent -u tom@tom.joindiaspora.com:evankorth http://tom.joindiaspora.com/zombiefriends" run "curl -silent -u tom@tom.joindiaspora.com:evankorth http://tom.joindiaspora.com/zombiefriends"
sleep(10)
backers.each do |backer| backers.each do |backer|
run "curl -silent -u #{backer['username']}@#{backer['username']}.joindiaspora.com:#{backer['username']}#{backer['pin']} http://#{backer['username']}.joindiaspora.com/zombiefriendaccept" run "curl -silent -u #{backer['username']}@#{backer['username']}.joindiaspora.com:#{backer['username']}#{backer['pin']} http://#{backer['username']}.joindiaspora.com/zombiefriendaccept"
#run "curl -silent -u #{backer['username']}@#{backer['username']}.joindiaspora.com:#{backer['username']}#{backer['pin']} http://#{backer['username']}.joindiaspora.com/set_profile_photo" #run "curl -silent -u #{backer['username']}@#{backer['username']}.joindiaspora.com:#{backer['username']}#{backer['pin']} http://#{backer['username']}.joindiaspora.com/set_profile_photo"

View file

@ -6,7 +6,7 @@ cross_server:
deploy_to: '/usr/local/app/diaspora' deploy_to: '/usr/local/app/diaspora'
user: 'root' user: 'root'
repo: 'git://github.com/diaspora/diaspora.git' repo: 'git://github.com/diaspora/diaspora.git'
branch: 'master' branch: 'em-webfinger'
default_env: 'development' default_env: 'development'
servers: servers:
tom: tom:

View file

@ -1,12 +1,18 @@
require File.join(Rails.root, 'lib/em-webfinger')
module Diaspora module Diaspora
module UserModules module UserModules
module Receiving module Receiving
def receive_salmon salmon_xml def receive_salmon salmon_xml
salmon = Salmon::SalmonSlap.parse salmon_xml, self salmon = Salmon::SalmonSlap.parse salmon_xml, self
if salmon.verified_for_key?(salmon.author.public_key) webfinger = EMWebfinger.new(salmon.author_email)
Rails.logger.info("data in salmon: #{salmon.parsed_data}")
self.receive(salmon.parsed_data, salmon.author) webfinger.on_person { |salmon_author|
end if salmon.verified_for_key?(salmon_author.public_key)
Rails.logger.info("data in salmon: #{salmon.parsed_data}")
self.receive(salmon.parsed_data, salmon_author)
end
}
end end
def receive xml, salmon_author def receive xml, salmon_author

85
lib/em-webfinger.rb Normal file
View file

@ -0,0 +1,85 @@
require File.join(Rails.root, 'lib/hcard')
require File.join(Rails.root, 'lib/webfinger_profile')
class EMWebfinger
TIMEOUT = 5
def initialize(account)
@account = account
@callbacks = []
# Raise an error if identifier has a port number
raise "Identifier is invalid" if(@account.strip.match(/\:\d+$/))
# Raise an error if identifier is not a valid email (generous regexp)
raise "Identifier is invalid" if !(@account=~ /\A.*\@.*\..*\Z/)
end
def fetch
raise 'you need to set a callback before calling fetch' if @callbacks.empty?
query = /\A^#{Regexp.escape(@account.gsub('acct:', '').to_s)}\z/i
local_person = Person.first(:diaspora_handle => query)
person = Person.by_account_identifier(@account)
if person
process_callbacks person
else
get_xrd
end
end
def on_person(&block)
@callbacks << block
self.fetch
end
private
def get_xrd
http = EventMachine::HttpRequest.new(xrd_url).get :timeout => TIMEOUT
http.callback { get_webfinger_profile(webfinger_profile_url(http.response)) }
http.errback { process_callbacks "there was an error getting the xrd at #{xrd_url}" }
end
def get_webfinger_profile(profile_url)
http = EventMachine::HttpRequest.new(profile_url).get :timeout => TIMEOUT
http.callback{ make_person_from_webfinger(http.response) }
http.errback{ process_callbacks "failed to fetch webfinger profile for #{profile_url}"}
end
def make_person_from_webfinger(webfinger_profile)
unless webfinger_profile.strip == ""
wf_profile = WebfingerProfile.new(@account, webfinger_profile)
http = EventMachine::HttpRequest.new(wf_profile.hcard).get :timeout => TIMEOUT
http.callback{
hcard = HCard.build http.response
p = Person.build_from_webfinger(wf_profile, hcard)
process_callbacks(p)
}
http.errback{process_callbacks "there was a problem fetching the hcard for #{@account}"}
end
end
def process_callbacks(person)
@callbacks.each { |c| c.call(person) }
end
##helpers
private
def webfinger_profile_url(xrd_response)
doc = Nokogiri::XML::Document.parse(xrd_response)
swizzle doc.at('Link[rel=lrdd]').attribute('template').value
end
def xrd_url(ssl = false)
domain = @account.split('@')[1]
"http#{'s' if ssl}://#{domain}/.well-known/host-meta"
end
def swizzle(template)
template.gsub '{uri}', @account
end
end

View file

@ -8,13 +8,19 @@ module HCard
end end
def self.parse doc def self.parse doc
{:given_name => doc.css(".given_name").text, {
:family_name => doc.css(".family_name").text, :given_name => doc.css(".given_name").text,
:url => doc.css("#pod_location").text, :family_name => doc.css(".family_name").text,
:photo => doc.css(".photo[src]").attribute('src').text } :url => doc.css("#pod_location").text,
:photo => doc.css(".photo[src]").attribute('src').text
}
end end
def self.find url def self.find url
self.parse self.fetch(url) self.parse self.fetch(url)
end end
def self.build(raw_hcard)
self.parse Nokogiri::HTML(raw_hcard)
end
end end

View file

@ -110,11 +110,11 @@ HEADER
end end
def author def author
if @author if @author.nil?
@author @author ||= Person.by_account_identifier @author_email
else raise "did you remember to async webfinger?" if @author.nil?
@author ||= Person.by_webfinger @author_email
end end
@author
end end
# Decode URL-safe-Base64. This implements # Decode URL-safe-Base64. This implements
@ -130,10 +130,6 @@ HEADER
Base64.urlsafe_decode64 string Base64.urlsafe_decode64 string
end end
# def verified?
#
# end
# Check whether this envelope's signature can be verified with the # Check whether this envelope's signature can be verified with the
# provided OpenSSL::PKey::RSA public_key. # provided OpenSSL::PKey::RSA public_key.
# Example: # Example:

52
lib/webfinger_profile.rb Normal file
View file

@ -0,0 +1,52 @@
class WebfingerProfile
attr_accessor :webfinger_profile, :account, :links, :hcard, :guid, :public_key, :seed_location
def initialize(account, webfinger_profile)
@account = account
@webfinger_profile = webfinger_profile
@links = {}
set_fields
end
def valid_diaspora_profile?
!(@webfinger_profile.nil? || @account.nil? || @links.nil? || @hcard.nil? ||
@guid.nil? || @public_key.nil? || @seed_location.nil? )
end
private
def set_fields
doc = Nokogiri::XML.parse(webfinger_profile)
account_string = doc.css('Subject').text.gsub('acct:', '').strip
raise "account in profile(#{account_string}) and account requested (#{@account}) do not match" if account_string != @account
doc.css('Link').each do |l|
rel = text_of_attribute(l, 'rel')
href = text_of_attribute(l, 'href')
@links[rel] = href
case rel
when "http://microformats.org/profile/hcard"
@hcard = href
when "http://joindiaspora.com/guid"
@guid = href
when "http://joindiaspora.com/seed_location"
@seed_location = href
end
end
if doc.at('Link[rel=diaspora-public-key]')
begin
pubkey = text_of_attribute( doc.at('Link[rel=diaspora-public-key]'), 'href')
@public_key = Base64.decode64 pubkey
rescue Exception => e
puts "probally not diaspora..."
end
end
end
def text_of_attribute(doc, attr)
doc.attribute(attr) ? doc.attribute(attr).text : nil
end
end

View file

@ -97,7 +97,7 @@ describe PublicsController do
end end
it 'should add the pending request to the right user if the target person does not exist locally' do it 'should add the pending request to the right user if the target person does not exist locally' do
Person.should_receive(:by_webfinger).with(user2.person.diaspora_handle).and_return(user2.person) Person.should_receive(:by_account_identifier).with(user2.person.diaspora_handle).and_return(user2.person)
user2.person.delete user2.person.delete
user2.delete user2.delete
post :receive, :id => user.person.id, :xml => xml post :receive, :id => user.person.id, :xml => xml

View file

@ -19,7 +19,7 @@ describe RequestsController do
"aspect_id" => @user.aspects[0].id "aspect_id" => @user.aspects[0].id
} }
) )
response.should redirect_to aspect_path(@user.aspects[0].id.to_s) response.should redirect_to aspects_manage_path
end end
it "should not error out when requesting an invalid identity" do it "should not error out when requesting an invalid identity" do
@ -28,7 +28,7 @@ describe RequestsController do
"aspect_id" => @user.aspects[0].id "aspect_id" => @user.aspects[0].id
} }
) )
response.should redirect_to aspect_path(@user.aspects[0].id.to_s) response.should redirect_to aspects_manage_path
end end
it "should not error out when requesting an invalid identity with a port number" do it "should not error out when requesting an invalid identity with a port number" do
@ -37,7 +37,7 @@ describe RequestsController do
"aspect_id" => @user.aspects[0].id "aspect_id" => @user.aspects[0].id
} }
) )
response.should redirect_to aspect_path(@user.aspects[0].id.to_s) response.should redirect_to aspects_manage_path
end end
it "should not error out when requesting an identity from an invalid server" do it "should not error out when requesting an identity from an invalid server" do
@ -47,9 +47,10 @@ describe RequestsController do
"aspect_id" => @user.aspects[0].id "aspect_id" => @user.aspects[0].id
} }
) )
response.should redirect_to aspect_path(@user.aspects[0].id.to_s) response.should redirect_to aspects_manage_path
end end
it 'should redirect to the page which you called it from ' do
pending "i need to figure out how to do this"
end
end end

View file

@ -1,7 +1,7 @@
<?xml version='1.0' encoding='UTF-8'?> <?xml version='1.0' encoding='UTF-8'?>
<XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'> <XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'>
<Link rel='lrdd' <Link rel='lrdd'
template='http://example.com/webfinger/?q={uri}'> template='http://tom.joindiaspora.com/webfinger/?q={uri}'>
<Title>Resource Descriptor</Title> <Title>Resource Descriptor</Title>
</Link> </Link>
</XRD> </XRD>

View file

@ -6,29 +6,4 @@ require 'spec_helper'
describe RequestsHelper do describe RequestsHelper do
before do
stub_success("tom@tom.joindiaspora.com")
stub_success("evan@status.net")
@tom = Redfinger.finger('tom@tom.joindiaspora.com')
@evan = Redfinger.finger('evan@status.net')
end
describe "profile" do
it 'should detect how to subscribe to a diaspora or webfinger profile' do
subscription_mode(@tom).should == :friend
subscription_mode(@evan).should == :none
end
end
describe "#relationship_flow" do
let(:tom){ Factory(:user, :email => 'tom@tom.joindiaspora.com') }
before do
stub!(:current_user).and_return(tom)
end
it 'should return the correct tag and url for a given address' do
relationship_flow('tom@tom.joindiaspora.com')[:friend].receive_url.should include("receive/user")
end
end
end end

View file

@ -18,10 +18,8 @@ describe Diaspora::Parser do
comment = Factory.create(:comment, :post => post, :person => person, :diaspora_handle => person.diaspora_handle, :text => "Freedom!") comment = Factory.create(:comment, :post => post, :person => person, :diaspora_handle => person.diaspora_handle, :text => "Freedom!")
comment.delete comment.delete
xml = comment.to_diaspora_xml xml = comment.to_diaspora_xml
puts xml
comment_from_xml = Diaspora::Parser.from_xml(xml) comment_from_xml = Diaspora::Parser.from_xml(xml)
comment_from_xml.diaspora_handle person.diaspora_handle comment_from_xml.diaspora_handle.should == person.diaspora_handle
comment_from_xml.post.should == post comment_from_xml.post.should == post
comment_from_xml.text.should == "Freedom!" comment_from_xml.text.should == "Freedom!"
comment_from_xml.should_not be comment comment_from_xml.should_not be comment

View file

@ -0,0 +1,111 @@
# Copyright (c) 2010, Diaspora Inc. This file is
# licensed under the Affero General Public License version 3 or later. See
# the COPYRIGHT file.
require 'spec_helper'
require File.join(Rails.root, 'lib/em-webfinger')
describe EMWebfinger do
let(:user1) { Factory(:user) }
let(:user2) { Factory(:user) }
let(:account) {"foo@tom.joindiaspora.com"}
let(:person){ Factory(:person, :diaspora_handle => account)}
let(:finger){EMWebfinger.new(account)}
let(:good_request) { FakeHttpRequest.new(:success)}
let(:stub_good) {EventMachine::HttpRequest.stub!(:new).and_return(good_request)}
let(:stub_bad) {EventMachine::HttpRequest.stub!(:new).and_return(bad_request)}
let(:diaspora_xrd) {File.open(File.join(Rails.root, 'spec/fixtures/host_xrd')).read}
let(:diaspora_finger) {File.open(File.join(Rails.root, 'spec/fixtures/finger_xrd')).read}
let(:hcard_xml) {File.open(File.join(Rails.root, 'spec/fixtures/hcard_response')).read}
let(:non_diaspora_xrd) {File.open(File.join(Rails.root, 'spec/fixtures/nonseed_finger_xrd')).read}
let(:non_diaspora_hcard) {File.open(File.join(Rails.root, 'spec/fixtures/evan_hcard')).read}
context 'setup' do
let(:action){ Proc.new{|person| puts person.inspect }}
describe '#intialize' do
it 'sets account ' do
n = EMWebfinger.new("mbs348@gmail.com")
n.instance_variable_get(:@account).should_not be nil
end
it 'should raise an error on an unresonable email' do
proc{EMWebfinger.new("asfadfasdf")}.should raise_error
end
end
describe '#on_person' do
it 'should set a callback' do
n = EMWebfinger.new("mbs@gmail.com")
n.stub(:fetch).and_return(true)
n.on_person{|person| puts "foo"}
n.instance_variable_get(:@callbacks).count.should be 1
end
end
describe '#fetch' do
it 'should require a callback' do
proc{finger.fetch }.should raise_error "you need to set a callback before calling fetch"
end
end
context 'webfinger query chain processing' do
describe '#webfinger_profile_url' do
it 'should parse out the webfinger template' do
finger.send(:webfinger_profile_url, diaspora_xrd).should == "http://tom.joindiaspora.com/webfinger/?q=#{account}"
end
end
describe '#xrd_url' do
it 'should return canonical host-meta url' do
finger.send(:xrd_url).should == "http://tom.joindiaspora.com/.well-known/host-meta"
end
it 'can return the https version' do
finger.send(:xrd_url, true).should == "https://tom.joindiaspora.com/.well-known/host-meta"
end
end
end
context 'webfingering local people' do
it 'should return a person from the database if it matches its handle' do
person
EventMachine::HttpRequest.should_not_receive(:new)
EM.run do
finger.on_person { |p|
p.should == person
EM.stop
}
end
end
it 'should fetch a diaspora webfinger and make a person for them' do
good_request.callbacks = [diaspora_xrd, diaspora_finger, hcard_xml]
#new_person = Factory.build(:person, :diaspora_handle => "tom@tom.joindiaspora.com")
# http://tom.joindiaspora.com/.well-known/host-meta
f = EMWebfinger.new("tom@tom.joindiaspora.com")
EventMachine::HttpRequest.should_receive(:new).exactly(3).times.and_return(good_request)
EM.run {
f.on_person{ |p|
p.valid?.should be true
EM.stop
}
}
end
end
end
end

View file

@ -19,7 +19,8 @@ describe 'user encryption' do
deliverable = Object.new deliverable = Object.new
deliverable.stub!(:deliver) deliverable.stub!(:deliver)
Notifier.stub!(:new_request).and_return(deliverable) Notifier.stub!(:new_request).and_return(deliverable)
Person.should_receive(:by_webfinger).and_return(remote_user.person) Person.should_receive(:by_account_identifier).and_return(remote_user.person)
remote_user.should_receive(:push_to_people).and_return(true)
#should move this to friend request, but i found it here #should move this to friend request, but i found it here
id = remote_user.person.id id = remote_user.person.id
original_key = remote_user.exported_key original_key = remote_user.exported_key

View file

@ -5,7 +5,6 @@
require 'spec_helper' require 'spec_helper'
require File.join(Rails.root, 'lib/hcard') require File.join(Rails.root, 'lib/hcard')
describe HCard do describe HCard do
it 'should retreive and parse an hcard' do it 'should retreive and parse an hcard' do
stub_success("tom@tom.joindiaspora.com") stub_success("tom@tom.joindiaspora.com")
@ -13,6 +12,7 @@ describe HCard do
hcard = HCard.find f.hcard.first[:href] hcard = HCard.find f.hcard.first[:href]
hcard[:family_name].include?("Hamiltom").should be true hcard[:family_name].include?("Hamiltom").should be true
hcard[:given_name].include?("Alex").should be true hcard[:given_name].include?("Alex").should be true
hcard[:photo].include?("tom.jpg").should be true
hcard[:url].should == "http://tom.joindiaspora.com/" hcard[:url].should == "http://tom.joindiaspora.com/"
end end
end end

View file

@ -154,20 +154,3 @@ describe MessageHandler do
end end
end end
class FakeHttpRequest
def initialize(callback_wanted)
@callback = callback_wanted
end
def response
end
def post; end
def get; end
def callback(&b)
b.call if @callback == :success
end
def errback(&b)
b.call if @callback == :failure
end
end

View file

@ -65,25 +65,18 @@ describe Salmon do
end end
describe '#author' do describe '#author' do
before do
stub_success("tom@tom.joindiaspora.com")
end
it 'should reference a local author' do it 'should reference a local author' do
parsed_salmon.author.should == user.person parsed_salmon.author.should == user.person
end end
it 'should reference a remote author' do it 'should fail if no author is found' do
parsed_salmon.author_email = 'tom@tom.joindiaspora.com' parsed_salmon.author_email = 'tom@tom.joindiaspora.com'
parsed_salmon.author.public_key.should_not be_nil
proc {parsed_salmon.author.public_key}.should raise_error "did you remember to async webfinger?"
end end
it 'should fail to reference a nonexistent remote author' do
parsed_salmon.author_email = 'idsfug@difgubhpsduh.rgd'
proc {
Redfinger.stub(:finger).and_return(nil) #Redfinger returns nil when there is no profile
parsed_salmon.author.real_name}.should raise_error /No webfinger profile found/
end
end end
it 'verifies the signature for the sender' do it 'verifies the signature for the sender' do

View file

@ -0,0 +1,36 @@
require 'spec_helper'
describe WebfingerProfile do
let(:webfinger_profile){File.open(File.join(Rails.root, "spec/fixtures/finger_xrd")).read.strip}
let(:not_diaspora_webfinger){File.open(File.join(Rails.root, "spec/fixtures/nonseed_finger_xrd")).read.strip}
let(:account){"tom@tom.joindiaspora.com"}
let(:profile){ WebfingerProfile.new(account, webfinger_profile) }
context "parsing a diaspora profile" do
describe '#valid_diaspora_profile?' do
it 'should check all of the required fields' do
manual_nil_check(profile).should == profile.valid_diaspora_profile?
end
end
describe '#set_fields' do
it 'should check to make sure it has a the right webfinger profile' do
proc{ WebfingerProfile.new("nottom@tom.joindiaspora.com", webfinger_profile)}.should raise_error
end
it 'should handle a non-diaspora profile without blowing up' do
proc{ WebfingerProfile.new("evan@status.net", not_diaspora_webfinger)}.should_not raise_error
end
end
end
def manual_nil_check(profile)
profile.instance_variables.each do |var|
var = var.to_s.gsub('@', '')
return false if profile.send(var).nil? == true
end
true
end
end

View file

@ -117,7 +117,7 @@ describe Person do
end end
end end
describe '::search' do describe '#search' do
before do before do
@friend_one = Factory.create(:person) @friend_one = Factory.create(:person)
@friend_two = Factory.create(:person) @friend_two = Factory.create(:person)
@ -167,43 +167,48 @@ describe Person do
end end
end end
describe ".by_webfinger" do context 'people finders for webfinger' do
context "local people" do let(:user) {Factory(:user)}
before do let(:person) {Factory(:person)}
@local_person = Factory(:person)
Redfinger.should_not_receive :finger describe '.by_account_identifier' do
it 'should find a local users person' do
p = Person.by_account_identifier(user.diaspora_handle)
p.should == user.person
end end
it "finds the local person without calling out" do it 'should find remote users person' do
person = Person.by_webfinger(@local_person.diaspora_handle) p = Person.by_account_identifier(person.diaspora_handle)
person.should == @local_person p.should == person
end
it 'should downcase and strip the diaspora_handle' do
dh_upper = " " + user.diaspora_handle.upcase + " "
Person.by_account_identifier(dh_upper).should == user.person
end end
it "finds a local person with a mixed-case username" do it "finds a local person with a mixed-case username" do
user = Factory(:user, :username => "SaMaNtHa") user = Factory(:user, :username => "SaMaNtHa")
person = Person.by_webfinger(user.person.diaspora_handle) person = Person.by_account_identifier(user.person.diaspora_handle)
person.should == user.person person.should == user.person
end end
it "is case insensitive" do it "is case insensitive" do
user = Factory(:user, :username => "SaMaNtHa") user1 = Factory(:user, :username => "SaMaNtHa")
person = Person.by_webfinger(user.person.diaspora_handle.upcase) person = Person.by_account_identifier(user1.person.diaspora_handle.upcase)
person.should == user.person person.should == user1.person
end end
end
it 'should only find people who are exact matches' do it 'should only find people who are exact matches' do
user = Factory(:user, :username => "SaMaNtHa") user = Factory(:user, :username => "SaMaNtHa")
person = Factory(:person, :diaspora_handle => "tomtom@tom.joindiaspora.com") person = Factory(:person, :diaspora_handle => "tomtom@tom.joindiaspora.com")
user.person.diaspora_handle = "tom@tom.joindiaspora.com" user.person.diaspora_handle = "tom@tom.joindiaspora.com"
user.person.save user.person.save
Person.by_webfinger("tom@tom.joindiaspora.com").diaspora_handle.should == "tom@tom.joindiaspora.com" Person.by_account_identifier("tom@tom.joindiaspora.com").diaspora_handle.should == "tom@tom.joindiaspora.com"
end end
it 'should return nil if there is not an exact match' do
Redfinger.stub!(:finger).and_return(nil)
it 'should return nil if there is not an exact match' do
pending "should check in the webfinger client"
person = Factory(:person, :diaspora_handle => "tomtom@tom.joindiaspora.com") person = Factory(:person, :diaspora_handle => "tomtom@tom.joindiaspora.com")
person1 = Factory(:person, :diaspora_handle => "tom@tom.joindiaspora.comm") person1 = Factory(:person, :diaspora_handle => "tom@tom.joindiaspora.comm")
#Person.by_webfinger("tom@tom.joindiaspora.com").should_be false #Person.by_webfinger("tom@tom.joindiaspora.com").should_be false
@ -211,30 +216,45 @@ describe Person do
end end
it 'identifier should be a valid email' do it 'identifier should be a valid email' do
stub_success("joe.valid+email@my-address.com") pending "should check in the webfinger client"
Proc.new { stub_success("joe.valid+email@my-address.com")
Person.by_webfinger("joe.valid+email@my-address.com") Proc.new {
}.should_not raise_error(RuntimeError, "Identifier is invalid") Person.by_account_identifier("joe.valid+email@my-address.com")
}.should_not raise_error(RuntimeError, "Identifier is invalid")
stub_success("not_a_@valid_email") stub_success("not_a_@valid_email")
Proc.new { Proc.new {
Person.by_webfinger("not_a_@valid_email") Person.by_account_identifer("not_a_@valid_email")
}.should raise_error(RuntimeError, "Identifier is invalid") }.should raise_error(RuntimeError, "Identifier is invalid")
end
it 'should not accept a port number' do
pending "should check the webfinger client"
stub_success("eviljoe@diaspora.local:3000")
Proc.new {
Person.by_account_identifier('eviljoe@diaspora.local:3000')
}.should raise_error(RuntimeError, "Identifier is invalid")
end
end end
it 'should not accept a port number' do describe '.local_by_account_identifier' do
stub_success("eviljoe@diaspora.local:3000") it 'should find local users people' do
Proc.new { p = Person.local_by_account_identifier(user.diaspora_handle)
Person.by_webfinger('eviljoe@diaspora.local:3000') p.should == user.person
}.should raise_error(RuntimeError, "Identifier is invalid") end
end
it 'creates a stub for a remote user' do it 'should not find a remote person' do
stub_success("tom@tom.joindiaspora.com") p = Person.local_by_account_identifier(@person.diaspora_handle)
tom = Person.by_webfinger('tom@tom.joindiaspora.com') p.should be nil
tom.real_name.include?("Hamiltom").should be true end
it 'should call .by_account_identifier' do
Person.should_receive(:by_account_identifier)
Person.local_by_account_identifier(@person.diaspora_handle)
end
end end
end end
end end

View file

@ -79,12 +79,14 @@ describe User do
end end
it "keeps the original case" do it "keeps the original case" do
pending "do we want this?"
user = Factory.build(:user, :username => "WeIrDcAsE") user = Factory.build(:user, :username => "WeIrDcAsE")
user.should be_valid user.should be_valid
user.username.should == "WeIrDcAsE" user.username.should == "WeIrDcAsE"
end end
it "fails if the requested username is only different in case from an existing username" do it "fails if the requested username is only different in case from an existing username" do
pending "do we want this?"
duplicate_user = Factory.build(:user, :username => user.username.upcase) duplicate_user = Factory.build(:user, :username => user.username.upcase)
duplicate_user.should_not be_valid duplicate_user.should_not be_valid
end end

View file

@ -35,3 +35,34 @@ end
ImageUploader.enable_processing = false ImageUploader.enable_processing = false
class FakeHttpRequest
def initialize(callback_wanted)
@callback = callback_wanted
@callbacks = []
end
def callbacks=(rs)
@callbacks += rs.reverse
end
def response
@callbacks.pop unless @callbacks.nil? || @callbacks.empty?
end
def post(opts = nil);
self
end
def get(opts = nil)
self
end
def callback(&b)
b.call if @callback == :success
end
def errback(&b)
b.call if @callback == :failure
end
end