diff options
-rw-r--r-- | app/controllers/application_controller.rb | 1 | ||||
-rw-r--r-- | core/lib/extensions/couchrest.rb | 37 | ||||
-rw-r--r-- | users/app/controllers/webfinger_controller.rb | 19 | ||||
-rw-r--r-- | users/app/models/user.rb | 2 | ||||
-rw-r--r-- | users/app/views/users/_public_key_field.html.haml | 1 | ||||
-rw-r--r-- | users/app/views/users/edit.html.haml | 1 | ||||
-rw-r--r-- | users/app/views/webfinger/host_meta.xml.erb | 11 | ||||
-rw-r--r-- | users/app/views/webfinger/search.xml.erb | 7 | ||||
-rw-r--r-- | users/config/locales/en.yml | 2 | ||||
-rw-r--r-- | users/config/routes.rb | 2 | ||||
-rw-r--r-- | users/lib/leap_web_users/engine.rb | 2 | ||||
-rw-r--r-- | users/lib/webfinger.rb | 6 | ||||
-rw-r--r-- | users/lib/webfinger/host_meta_presenter.rb | 30 | ||||
-rw-r--r-- | users/lib/webfinger/user_presenter.rb | 35 | ||||
-rw-r--r-- | users/test/functional/webfinger_controller_test.rb | 33 | ||||
-rw-r--r-- | users/test/unit/webfinger/host_meta_presenter_test.rb | 24 | ||||
-rw-r--r-- | users/test/unit/webfinger/user_presenter_test.rb | 49 |
17 files changed, 248 insertions, 14 deletions
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index be7aa1f..06b245a 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -2,4 +2,5 @@ class ApplicationController < ActionController::Base protect_from_forgery ActiveSupport.run_load_hooks(:application_controller, self) + end diff --git a/core/lib/extensions/couchrest.rb b/core/lib/extensions/couchrest.rb index 5938df4..ca4b608 100644 --- a/core/lib/extensions/couchrest.rb +++ b/core/lib/extensions/couchrest.rb @@ -1,23 +1,32 @@ -module CouchRest::Model::Designs +module CouchRest + module Model::Designs - class View + class View - # so we can called Ticket.method.descending or Ticket.method.ascending - def ascending - self + # so we can called Ticket.method.descending or Ticket.method.ascending + def ascending + self + end end - end - class DesignMapper - def load_views(dir) - Dir.glob("#{dir}/*.js") do |js| - name = File.basename(js, '.js') - file = File.open(js, 'r') - view name.to_sym, - :map => file.read, - :reduce => "function(key, values, rereduce) { return sum(values); }" + class DesignMapper + def load_views(dir) + Dir.glob("#{dir}/*.js") do |js| + name = File.basename(js, '.js') + file = File.open(js, 'r') + view name.to_sym, + :map => file.read, + :reduce => "function(key, values, rereduce) { return sum(values); }" + end end end + end + class ModelRailtie + config.action_dispatch.rescue_responses.merge!( + 'CouchRest::Model::DocumentNotFound' => :not_found, + 'RestClient::ResourceNotFound' => :not_found + ) + end end diff --git a/users/app/controllers/webfinger_controller.rb b/users/app/controllers/webfinger_controller.rb new file mode 100644 index 0000000..8872802 --- /dev/null +++ b/users/app/controllers/webfinger_controller.rb @@ -0,0 +1,19 @@ +class WebfingerController < ApplicationController + + respond_to :xml, :json + layout false + + def host_meta + @host_meta = Webfinger::HostMetaPresenter.new(request) + respond_with @host_meta + end + + def search + username = params[:q].split('@')[0].to_s.downcase + user = User.find_by_login(username) + raise RECORD_NOT_FOUND, 'User not found' unless user.present? + @presenter = Webfinger::UserPresenter.new(user, request) + respond_with @presenter + end + +end diff --git a/users/app/models/user.rb b/users/app/models/user.rb index 292fb13..80d49a3 100644 --- a/users/app/models/user.rb +++ b/users/app/models/user.rb @@ -9,6 +9,8 @@ class User < CouchRest::Model::Base property :email_forward, String, :accessible => true property :email_aliases, [LocalEmail] + property :public_key, :accessible => true + validates :login, :password_salt, :password_verifier, :presence => true diff --git a/users/app/views/users/_public_key_field.html.haml b/users/app/views/users/_public_key_field.html.haml new file mode 100644 index 0000000..af88cbd --- /dev/null +++ b/users/app/views/users/_public_key_field.html.haml @@ -0,0 +1 @@ += f.input :public_key, :as => :text, :hint => t(:use_ascii_key), :input_html => {:class => "span5", :rows => 20} # will want to tweak this to be wide enough (maybe smaller text?) diff --git a/users/app/views/users/edit.html.haml b/users/app/views/users/edit.html.haml index 238c0eb..950a3b1 100644 --- a/users/app/views/users/edit.html.haml +++ b/users/app/views/users/edit.html.haml @@ -8,6 +8,7 @@ %legend=t :email_address The associated email address is = render @user.email_address, :as => :span + = user_form_with 'public_key_field', :legend => :public_key = user_form_with 'email_forward_field', :legend => :forward_email = user_form_with 'email_aliases', :legend => :add_email_alias = render 'tabs/tabs', :tabs => [:account, :email] diff --git a/users/app/views/webfinger/host_meta.xml.erb b/users/app/views/webfinger/host_meta.xml.erb new file mode 100644 index 0000000..cfcbcc0 --- /dev/null +++ b/users/app/views/webfinger/host_meta.xml.erb @@ -0,0 +1,11 @@ +<?xml version='1.0' encoding='UTF-8'?> + <XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'> + + <Subject><%= @host_meta.subject %></Subject> + + <%- @host_meta.links.each do |rel, link| %> + <Link rel='<%= rel %>' + type='<%= link[:type] %>' + template='<%= link[:template] %>' /> + <%- end %> + </XRD> diff --git a/users/app/views/webfinger/search.xml.erb b/users/app/views/webfinger/search.xml.erb new file mode 100644 index 0000000..7328552 --- /dev/null +++ b/users/app/views/webfinger/search.xml.erb @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0"> + <Subject><%= @presenter.subject %></Subject> + <%- @presenter.links.each do |rel, link| %> + <Link rel=<%=rel%> type=<%=link[:type]%> href="<%= link[:key] %>"/> + <% end %> +</XRD> diff --git a/users/config/locales/en.yml b/users/config/locales/en.yml index 7a6ab90..493b5db 100644 --- a/users/config/locales/en.yml +++ b/users/config/locales/en.yml @@ -13,10 +13,12 @@ en: set_email_address: "Set email address" forward_email: "Forward email" email_aliases: "Email aliases" + public_key: "Public Key" add_email_alias: "Add email alias" user_updated_successfully: "Settings have been updated successfully." user_created_successfully: "Successfully created your account." email_alias_destroyed_successfully: "Successfully removed the alias '%{alias}'." + use_ascii_key: "Use ASCII-armored PGP key" activemodel: models: diff --git a/users/config/routes.rb b/users/config/routes.rb index 0c2d8d9..4127862 100644 --- a/users/config/routes.rb +++ b/users/config/routes.rb @@ -18,4 +18,6 @@ Rails.application.routes.draw do resources :email_aliases, :only => [:destroy], :id => /.*/ end + get "/.well-known/host-meta" => 'webfinger#host_meta' + get "/webfinger" => 'webfinger#search' end diff --git a/users/lib/leap_web_users/engine.rb b/users/lib/leap_web_users/engine.rb index 7033576..f8ed71c 100644 --- a/users/lib/leap_web_users/engine.rb +++ b/users/lib/leap_web_users/engine.rb @@ -7,6 +7,8 @@ require "ruby-srp" require "warden/session_serializer" require "warden/strategies/secure_remote_password" +require "webfinger" + module LeapWebUsers class Engine < ::Rails::Engine diff --git a/users/lib/webfinger.rb b/users/lib/webfinger.rb new file mode 100644 index 0000000..dd49b41 --- /dev/null +++ b/users/lib/webfinger.rb @@ -0,0 +1,6 @@ +module Webfinger + + autoload :HostMetaPresenter, 'webfinger/host_meta_presenter' + autoload :UserPresenter, 'webfinger/user_presenter' + +end diff --git a/users/lib/webfinger/host_meta_presenter.rb b/users/lib/webfinger/host_meta_presenter.rb new file mode 100644 index 0000000..84ab7a9 --- /dev/null +++ b/users/lib/webfinger/host_meta_presenter.rb @@ -0,0 +1,30 @@ +require 'uri' + +class Webfinger::HostMetaPresenter + def initialize(request) + @request = request + end + + def to_json(options = {}) + { + subject: subject, + links: links + }.to_json(options) + end + + def subject + url = URI.parse(@request.url) + url.path = '' + url.to_s + end + + def links + { lrdd: { type: 'application/xrd+xml', template: webfinger_template } } + end + + protected + + def webfinger_template(path = 'webfinger', query_param='q') + "#{subject}/#{path}?#{query_param}={uri}" + end +end diff --git a/users/lib/webfinger/user_presenter.rb b/users/lib/webfinger/user_presenter.rb new file mode 100644 index 0000000..329f477 --- /dev/null +++ b/users/lib/webfinger/user_presenter.rb @@ -0,0 +1,35 @@ +class Webfinger::UserPresenter + include Rails.application.routes.url_helpers + attr_accessor :user + + def initialize(user, request) + @user = user + @request = request + end + + def to_json(options = {}) + { + subject: subject, + links: links + }.to_json(options) + end + + def subject + "acct:#{@user.email_address}" + end + + def links + links = {} + links[:public_key] = { type: 'PGP', href: key } if key + return links + end + + protected + + def key + if @user.public_key.present? + Base64.encode64(@user.public_key.to_s) + end + end + +end diff --git a/users/test/functional/webfinger_controller_test.rb b/users/test/functional/webfinger_controller_test.rb new file mode 100644 index 0000000..6597b69 --- /dev/null +++ b/users/test/functional/webfinger_controller_test.rb @@ -0,0 +1,33 @@ +require 'test_helper' + +class WebfingerControllerTest < ActionController::TestCase + + test "get host meta xml" do + get :host_meta, :format => :xml + assert_response :success + assert_equal "application/xml", response.content_type + end + + test "get host meta json" do + get :host_meta, :format => :json + assert_response :success + assert_equal "application/json", response.content_type + end + + test "get user webfinger xml" do + @user = stub_record :user, :public_key => 'my public key' + User.stubs(:find_by_login).with(@user.login).returns(@user) + get :search, :q => @user.email_address.to_s, :format => :xml + assert_response :success + assert_equal "application/xml", response.content_type + end + + test "get user webfinger json" do + @user = stub_record :user, :public_key => 'my public key' + User.stubs(:find_by_login).with(@user.login).returns(@user) + get :search, :q => @user.email_address.to_s, :format => :json + assert_response :success + assert_equal "application/json", response.content_type + end + +end diff --git a/users/test/unit/webfinger/host_meta_presenter_test.rb b/users/test/unit/webfinger/host_meta_presenter_test.rb new file mode 100644 index 0000000..af86404 --- /dev/null +++ b/users/test/unit/webfinger/host_meta_presenter_test.rb @@ -0,0 +1,24 @@ +require 'test_helper' +require 'webfinger' +require 'json' + +class Webfinger::HostMetaPresenterTest < ActiveSupport::TestCase + + setup do + @request = stub( + url: "https://#{APP_CONFIG[:domain]}/.well-known/host-meta" + ) + @meta = Webfinger::HostMetaPresenter.new(@request) + end + + test "creates proper json" do + hash = JSON.parse @meta.to_json + assert_equal ["subject", "links"].sort, hash.keys.sort + hash.each do |key, value| + assert_equal @meta.send(key.to_sym).to_json, value.to_json + end + end + +end + + diff --git a/users/test/unit/webfinger/user_presenter_test.rb b/users/test/unit/webfinger/user_presenter_test.rb new file mode 100644 index 0000000..04aeb22 --- /dev/null +++ b/users/test/unit/webfinger/user_presenter_test.rb @@ -0,0 +1,49 @@ +require 'test_helper' +require 'webfinger' +require 'json' + +class Webfinger::UserPresenterTest < ActiveSupport::TestCase + + + setup do + @user = stub( + username: 'testuser', + email_address: "testuser@#{APP_CONFIG[:domain]}" + ) + @request = stub( + host: APP_CONFIG[:domain] + ) + end + + test "user without key has no links" do + @user.stubs :public_key => nil + presenter = Webfinger::UserPresenter.new(@user, @request) + assert_equal Hash.new, presenter.links + end + + test "user with key has corresponding link" do + @user.stubs :public_key => "here's a key" + presenter = Webfinger::UserPresenter.new(@user, @request) + assert_equal [:public_key], presenter.links.keys + assert_equal "PGP", presenter.links[:public_key][:type] + assert_equal presenter.send(:key), presenter.links[:public_key][:href] + end + + test "key is base64 encoded" do + @user.stubs :public_key => "here's a key" + presenter = Webfinger::UserPresenter.new(@user, @request) + assert_equal Base64.encode64(@user.public_key), presenter.send(:key) + end + + test "creates proper json representation" do + @user.stubs :public_key => "here's a key" + presenter = Webfinger::UserPresenter.new(@user, @request) + hash = JSON.parse presenter.to_json + assert_equal ["subject", "links"].sort, hash.keys.sort + hash.each do |key, value| + assert_equal presenter.send(key.to_sym).to_json, value.to_json + end + end + + +end |