diff options
author | jessib <jessib@riseup.net> | 2014-02-10 10:27:52 -0800 |
---|---|---|
committer | jessib <jessib@riseup.net> | 2014-02-10 10:27:52 -0800 |
commit | b6ef51277b4e6d65cfda15f0124ae4f222f7f241 (patch) | |
tree | da7eb2d3a3a648be0be519aae23f997f248ba320 | |
parent | bcdde2f6bfb4ed3a1535bd2e50ab47529a9141e2 (diff) | |
parent | b4719619aabbe9ebf74563b62e1eb8e4fb248c21 (diff) |
Merge pull request #138 from azul/feature/token-only-api-auth
Feature/token only api auth
30 files changed, 419 insertions, 281 deletions
diff --git a/billing/app/controllers/billing_admin_controller.rb b/billing/app/controllers/billing_admin_controller.rb index cd6149f..e11d4ee 100644 --- a/billing/app/controllers/billing_admin_controller.rb +++ b/billing/app/controllers/billing_admin_controller.rb @@ -1,5 +1,5 @@ class BillingAdminController < BillingBaseController - before_filter :authorize_admin + before_filter :require_admin def show diff --git a/billing/app/controllers/credit_card_info_controller.rb b/billing/app/controllers/credit_card_info_controller.rb index 717fa18..fbaa6f1 100644 --- a/billing/app/controllers/credit_card_info_controller.rb +++ b/billing/app/controllers/credit_card_info_controller.rb @@ -1,5 +1,5 @@ class CreditCardInfoController < ApplicationController - before_filter :authorize, :set_user + before_filter :require_login, :set_user def edit @credit_card = Braintree::CreditCard.find(params[:id]) diff --git a/billing/app/controllers/customer_controller.rb b/billing/app/controllers/customer_controller.rb index 901cb34..6cbcb44 100644 --- a/billing/app/controllers/customer_controller.rb +++ b/billing/app/controllers/customer_controller.rb @@ -1,5 +1,5 @@ class CustomerController < BillingBaseController - before_filter :authorize, :fetch_customer + before_filter :require_login, :fetch_customer def show if @customer diff --git a/billing/app/controllers/payments_controller.rb b/billing/app/controllers/payments_controller.rb index 0b5abe7..fce6570 100644 --- a/billing/app/controllers/payments_controller.rb +++ b/billing/app/controllers/payments_controller.rb @@ -1,5 +1,5 @@ class PaymentsController < BillingBaseController - before_filter :authorize, :only => [:index] + before_filter :require_login, :only => [:index] def new fetch_transparent_redirect diff --git a/billing/app/controllers/subscriptions_controller.rb b/billing/app/controllers/subscriptions_controller.rb index 01aaab4..f066b3c 100644 --- a/billing/app/controllers/subscriptions_controller.rb +++ b/billing/app/controllers/subscriptions_controller.rb @@ -1,5 +1,5 @@ class SubscriptionsController < BillingBaseController - before_filter :authorize + before_filter :require_login before_filter :fetch_subscription, :only => [:show, :destroy] before_filter :confirm_cancel_subscription, :only => [:destroy] before_filter :confirm_self_or_admin, :only => [:index] diff --git a/billing/test/integration/admin_customer_test.rb b/billing/test/integration/admin_customer_test.rb index 58a7557..1b9953f 100644 --- a/billing/test/integration/admin_customer_test.rb +++ b/billing/test/integration/admin_customer_test.rb @@ -14,8 +14,8 @@ class AdminCustomerTest < ActionDispatch::IntegrationTest teardown do Warden.test_reset! - @user.destroy - @admin.destroy + @user.destroy if @user + @admin.destroy if @admin end test "check non customer as admin" do diff --git a/certs/app/controllers/certs_controller.rb b/certs/app/controllers/certs_controller.rb index 62ef3fd..82cbc44 100644 --- a/certs/app/controllers/certs_controller.rb +++ b/certs/app/controllers/certs_controller.rb @@ -1,6 +1,6 @@ class CertsController < ApplicationController - before_filter :login_if_required + before_filter :require_login, :unless => :anonymous_certs_allowed? # GET /cert def show @@ -10,10 +10,9 @@ class CertsController < ApplicationController protected - def login_if_required - authorize unless APP_CONFIG[:allow_anonymous_certs] + def anonymous_certs_allowed? + APP_CONFIG[:allow_anonymous_certs] end - # # this is some temporary logic until we store the service level in the user db. # diff --git a/core/lib/extensions/testing.rb b/core/lib/extensions/testing.rb index aad7fc1..8f7e73c 100644 --- a/core/lib/extensions/testing.rb +++ b/core/lib/extensions/testing.rb @@ -22,6 +22,8 @@ module LeapWebCore end def assert_json_response(object) + assert_equal 'application/json', + get_response.content_type.to_s.split(';').first if object.is_a? Hash object.stringify_keys! if object.respond_to? :stringify_keys! assert_equal object, json_response diff --git a/core/test/support/browser_integration_test.rb b/core/test/support/browser_integration_test.rb new file mode 100644 index 0000000..2885c3a --- /dev/null +++ b/core/test/support/browser_integration_test.rb @@ -0,0 +1,81 @@ +# +# BrowserIntegrationTest +# +# Use this class for capybara based integration tests for the ui. +# + +class BrowserIntegrationTest < ActionDispatch::IntegrationTest + + CONFIG_RU = (Rails.root + 'config.ru').to_s + OUTER_APP = Rack::Builder.parse_file(CONFIG_RU).first + + require 'capybara/poltergeist' + + Capybara.register_driver :rack_test do |app| + Capybara::RackTest::Driver.new(app) + end + + Capybara.register_driver :poltergeist do |app| + Capybara::Poltergeist::Driver.new(app) + end + + # this is integration testing. So let's make the whole + # rack stack available... + Capybara.app = OUTER_APP + Capybara.run_server = true + Capybara.app_host = 'http://lvh.me:3003' + Capybara.server_port = 3003 + Capybara.javascript_driver = :poltergeist + Capybara.default_wait_time = 5 + + + # Make the Capybara DSL available + include Capybara::DSL + + setup do + Capybara.current_driver = Capybara.javascript_driver + page.driver.add_headers 'ACCEPT-LANGUAGE' => 'en-EN' + end + + teardown do + Capybara.reset_sessions! # Forget the (simulated) browser state + Capybara.use_default_driver # Revert Capybara.current_driver to Capybara.default_driver + end + + def submit_signup(username = nil, password = nil) + username ||= "test_#{SecureRandom.urlsafe_base64}".downcase + password ||= SecureRandom.base64 + visit '/users/new' + fill_in 'Username', with: username + fill_in 'Password', with: password + fill_in 'Password confirmation', with: password + click_on 'Sign Up' + return username, password + end + + add_teardown_hook do |testcase| + unless testcase.passed? + testcase.save_state + end + end + + def save_state + page.save_screenshot screenshot_path + File.open(logfile_path, 'w') do |test_log| + test_log.puts self.class.name + test_log.puts "=========================" + test_log.puts __name__ + test_log.puts Time.now + test_log.puts current_path + test_log.puts page.status_code + test_log.puts page.response_headers + test_log.puts "page.html" + test_log.puts "------------------------" + test_log.puts page.html + test_log.puts "server log" + test_log.puts "------------------------" + test_log.puts `tail log/test.log -n 200` + end + end + +end diff --git a/core/test/support/rack_test.rb b/core/test/support/rack_test.rb new file mode 100644 index 0000000..2d8e5c4 --- /dev/null +++ b/core/test/support/rack_test.rb @@ -0,0 +1,37 @@ +class RackTest < ActiveSupport::TestCase + include Rack::Test::Methods + include Warden::Test::Helpers + include LeapWebCore::AssertResponses + + CONFIG_RU = (Rails.root + 'config.ru').to_s + OUTER_APP = Rack::Builder.parse_file(CONFIG_RU).first + + def app + OUTER_APP + end + + def assert_access_denied + assert_json_response('error' => I18n.t(:not_authorized)) + assert_response :unprocessable_entity + end + + # inspired by rails 4 + # -> actionpack/lib/action_dispatch/testing/assertions/response.rb + def assert_response(type, message = nil) + # RackTest does not know @response + response_code = last_response.status + message ||= "Expected response to be a <#{type}>, but was <#{response_code}>" + + if Symbol === type + if [:success, :missing, :redirect, :error].include?(type) + assert last_response.send("#{type}?"), message + else + code = Rack::Utils::SYMBOL_TO_STATUS_CODE[type] + assert_equal code, response_code, message + end + else + assert_equal type, response_code, message + end + end + +end diff --git a/help/app/controllers/tickets_controller.rb b/help/app/controllers/tickets_controller.rb index c193ff4..d65ee43 100644 --- a/help/app/controllers/tickets_controller.rb +++ b/help/app/controllers/tickets_controller.rb @@ -4,7 +4,7 @@ class TicketsController < ApplicationController respond_to :html, :json #has_scope :open, :type => boolean - before_filter :authorize, :only => [:index] + before_filter :require_login, :only => [:index] before_filter :fetch_ticket, :only => [:show, :update, :destroy] # don't now have an edit method before_filter :fetch_user before_filter :set_title diff --git a/help/test/functional/tickets_controller_test.rb b/help/test/functional/tickets_controller_test.rb index 2530ba1..416fb73 100644 --- a/help/test/functional/tickets_controller_test.rb +++ b/help/test/functional/tickets_controller_test.rb @@ -2,6 +2,11 @@ require 'test_helper' class TicketsControllerTest < ActionController::TestCase + teardown do + # destroy all tickets that were created during the test + Ticket.all.each{|t| t.destroy} + end + test "should get index if logged in" do login get :index @@ -64,7 +69,6 @@ class TicketsControllerTest < ActionController::TestCase assert_equal 1, assigns(:ticket).comments.count assert_nil assigns(:ticket).comments.first.posted_by - assigns(:ticket).destroy # destroys without checking permission. is that okay? end @@ -87,7 +91,6 @@ class TicketsControllerTest < ActionController::TestCase assert_equal 1, assigns(:ticket).comments.count assert_not_nil assigns(:ticket).comments.first.posted_by assert_equal assigns(:ticket).comments.first.posted_by, @current_user.id - assigns(:ticket).destroy end test "add comment to unauthenticated ticket" do @@ -101,7 +104,6 @@ class TicketsControllerTest < ActionController::TestCase assert_equal ticket, assigns(:ticket) # still same ticket, with different comments assert_not_equal ticket.comments, assigns(:ticket).comments # ticket == assigns(:ticket), but they have different comments (which we want) - assigns(:ticket).destroy end @@ -118,7 +120,6 @@ class TicketsControllerTest < ActionController::TestCase assert_not_equal ticket.comments, assigns(:ticket).comments assert_not_nil assigns(:ticket).comments.last.posted_by assert_equal assigns(:ticket).comments.last.posted_by, @current_user.id - assigns(:ticket).destroy end @@ -153,12 +154,9 @@ class TicketsControllerTest < ActionController::TestCase assert_not_equal ticket.comments, assigns(:ticket).comments assert_not_nil assigns(:ticket).comments.last.posted_by assert_equal assigns(:ticket).comments.last.posted_by, @current_user.id - - assigns(:ticket).destroy end test "tickets by admin" do - begin other_user = find_record :user ticket = FactoryGirl.create :ticket, :created_by => other_user.id @@ -173,9 +171,6 @@ class TicketsControllerTest < ActionController::TestCase assigns(:tickets).first.save get :index, {:admin_status => "all", :open_status => "open"} end - ensure - ticket.reload.destroy if ticket - end end @@ -188,7 +183,6 @@ class TicketsControllerTest < ActionController::TestCase assert assigns(:all_tickets).include?(testticket) get :index, {:user_id => user.id, :open_status => "open"} assert !assigns(:all_tickets).include?(testticket) - testticket.destroy end test "commenting on a ticket adds to tickets that are mine" do @@ -204,8 +198,6 @@ class TicketsControllerTest < ActionController::TestCase assert assigns(:all_tickets).include?(assigns(:ticket)) assert_not_nil assigns(:ticket).comments.last.posted_by assert_equal assigns(:ticket).comments.last.posted_by, @current_user.id - - assigns(:ticket).destroy end test "admin ticket ordering" do @@ -228,7 +220,6 @@ class TicketsControllerTest < ActionController::TestCase assert_not_equal first_tick, assigns(:all_tickets).first assert_not_equal last_tick, assigns(:all_tickets).last - tickets.each {|ticket| ticket.destroy} end test "tickets for regular user" do @@ -275,7 +266,6 @@ class TicketsControllerTest < ActionController::TestCase assert assigns(:all_tickets).include?(other_ticket) assert_equal assigns(:all_tickets).count, number_closed_tickets + number_open_tickets - assigns(:all_tickets).each {|t| t.destroy} end diff --git a/test/test_helper.rb b/test/test_helper.rb index 3fb2716..f63591f 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -10,74 +10,6 @@ Dir["#{File.dirname(__FILE__)}/../*/test/support/**/*.rb"].each { |f| require f class ActiveSupport::TestCase # Add more helper methods to be used by all tests here... - def file_path(name) - File.join(Rails.root, 'test', 'files', name) - end - -end - -require 'capybara/poltergeist' - -CONFIG_RU = (Rails.root + 'config.ru').to_s -OUTER_APP = Rack::Builder.parse_file(CONFIG_RU).first - -Capybara.register_driver :rack_test do |app| - Capybara::RackTest::Driver.new(app) -end - -Capybara.register_driver :poltergeist do |app| - Capybara::Poltergeist::Driver.new(app) -end - -# this is integration testing. So let's make the whole -# rack stack available... -Capybara.app = OUTER_APP -Capybara.run_server = true -Capybara.app_host = 'http://lvh.me:3003' -Capybara.server_port = 3003 -Capybara.javascript_driver = :poltergeist -Capybara.default_wait_time = 5 - -class BrowserIntegrationTest < ActionDispatch::IntegrationTest - # Make the Capybara DSL available - include Capybara::DSL - include IntegrationTestHelper - - setup do - Capybara.current_driver = Capybara.javascript_driver - page.driver.add_headers 'ACCEPT-LANGUAGE' => 'en-EN' - end - - teardown do - Capybara.reset_sessions! # Forget the (simulated) browser state - Capybara.use_default_driver # Revert Capybara.current_driver to Capybara.default_driver - end - - add_teardown_hook do |testcase| - unless testcase.passed? - testcase.save_state - end - end - - def save_state - page.save_screenshot screenshot_path - File.open(logfile_path, 'w') do |test_log| - test_log.puts self.class.name - test_log.puts "=========================" - test_log.puts __name__ - test_log.puts Time.now - test_log.puts current_path - test_log.puts page.status_code - test_log.puts page.response_headers - test_log.puts "page.html" - test_log.puts "------------------------" - test_log.puts page.html - test_log.puts "server log" - test_log.puts "------------------------" - test_log.puts `tail log/test.log -n 200` - end - end - protected def logfile_path @@ -87,4 +19,9 @@ class BrowserIntegrationTest < ActionDispatch::IntegrationTest def screenshot_path Rails.root + 'tmp' + "#{self.class.name.underscore}.#{__name__}.png" end + + def file_path(name) + File.join(Rails.root, 'test', 'files', name) + end + end diff --git a/users/app/controllers/controller_extension/authentication.rb b/users/app/controllers/controller_extension/authentication.rb index d831fbe..e83d6b2 100644 --- a/users/app/controllers/controller_extension/authentication.rb +++ b/users/app/controllers/controller_extension/authentication.rb @@ -15,7 +15,7 @@ module ControllerExtension::Authentication !!current_user end - def authorize + def require_login access_denied unless logged_in? end @@ -38,7 +38,7 @@ module ControllerExtension::Authentication current_user && current_user.is_admin? end - def authorize_admin + def require_admin access_denied unless admin? end diff --git a/users/app/controllers/controller_extension/token_authentication.rb b/users/app/controllers/controller_extension/token_authentication.rb index 530294a..6e0a6ce 100644 --- a/users/app/controllers/controller_extension/token_authentication.rb +++ b/users/app/controllers/controller_extension/token_authentication.rb @@ -1,11 +1,18 @@ module ControllerExtension::TokenAuthentication extend ActiveSupport::Concern - def token_authenticate - authenticate_with_http_token do |token_id, options| - @token = Token.find(token_id) + def token + @token ||= authenticate_with_http_token do |token_id, options| + Token.find(token_id) end - @token.authenticate if @token + end + + def token_authenticate + @token_authenticated ||= token.authenticate if token + end + + def require_token + access_denied unless token_authenticate end def logout @@ -14,10 +21,7 @@ module ControllerExtension::TokenAuthentication end def clear_token - authenticate_with_http_token do |token_id, options| - @token = Token.find(token_id) - @token.destroy if @token - end + token.destroy if token end end diff --git a/users/app/controllers/users_controller.rb b/users/app/controllers/users_controller.rb index a5461cd..6b32d49 100644 --- a/users/app/controllers/users_controller.rb +++ b/users/app/controllers/users_controller.rb @@ -4,9 +4,9 @@ class UsersController < UsersBaseController - before_filter :authorize, :only => [:show, :edit, :update, :destroy] + before_filter :require_login, :except => [:new] + before_filter :require_admin, :only => [:index, :deactivate, :enable] before_filter :fetch_user, :only => [:show, :edit, :update, :destroy, :deactivate, :enable] - before_filter :authorize_admin, :only => [:index, :deactivate, :enable] respond_to :html diff --git a/users/app/controllers/v1/messages_controller.rb b/users/app/controllers/v1/messages_controller.rb index 1b994ca..90986e2 100644 --- a/users/app/controllers/v1/messages_controller.rb +++ b/users/app/controllers/v1/messages_controller.rb @@ -2,7 +2,7 @@ module V1 class MessagesController < ApplicationController skip_before_filter :verify_authenticity_token - before_filter :authorize + before_filter :require_token respond_to :json diff --git a/users/app/controllers/v1/sessions_controller.rb b/users/app/controllers/v1/sessions_controller.rb index eb6c322..eae3a1e 100644 --- a/users/app/controllers/v1/sessions_controller.rb +++ b/users/app/controllers/v1/sessions_controller.rb @@ -2,6 +2,7 @@ module V1 class SessionsController < ApplicationController skip_before_filter :verify_authenticity_token + before_filter :require_token, only: :destroy def new @session = Session.new diff --git a/users/app/controllers/v1/users_controller.rb b/users/app/controllers/v1/users_controller.rb index 0903888..8897d01 100644 --- a/users/app/controllers/v1/users_controller.rb +++ b/users/app/controllers/v1/users_controller.rb @@ -3,8 +3,8 @@ module V1 skip_before_filter :verify_authenticity_token before_filter :fetch_user, :only => [:update] - before_filter :authorize, :only => [:update] - before_filter :authorize_admin, :only => [:index] + before_filter :require_admin, :only => [:index] + before_filter :require_token, :only => [:update] respond_to :json diff --git a/users/test/functional/application_controller_test.rb b/users/test/functional/application_controller_test.rb index 94b77bd..c4c922b 100644 --- a/users/test/functional/application_controller_test.rb +++ b/users/test/functional/application_controller_test.rb @@ -7,21 +7,21 @@ class ApplicationControllerTest < ActionController::TestCase @controller.response = @response end - def test_authorize_redirect - @controller.send(:authorize) + def test_require_login_redirect + @controller.send(:require_login) assert_access_denied(true, false) end - def test_authorized + def test_require_login login - @controller.send(:authorize) + @controller.send(:require_login) assert_access_denied(false) end - def test_authorize_admin + def test_require_admin login @current_user.expects(:is_admin?).returns(false) - @controller.send(:authorize_admin) + @controller.send(:require_admin) assert_access_denied end diff --git a/users/test/functional/v1/sessions_controller_test.rb b/users/test/functional/v1/sessions_controller_test.rb index 4200e8f..df0d681 100644 --- a/users/test/functional/v1/sessions_controller_test.rb +++ b/users/test/functional/v1/sessions_controller_test.rb @@ -36,7 +36,7 @@ class V1::SessionsControllerTest < ActionController::TestCase post :create, :login => @user.login, 'A' => @client_hex end - test "should authorize" do + test "should authenticate" do request.env['warden'].expects(:authenticate!) @controller.stubs(:current_user).returns(@user) handshake = stub(:to_hash => {h: "ash"}) diff --git a/users/test/integration/api/account_flow_test.rb b/users/test/integration/api/account_flow_test.rb deleted file mode 100644 index edd0859..0000000 --- a/users/test/integration/api/account_flow_test.rb +++ /dev/null @@ -1,136 +0,0 @@ -require 'test_helper' -require_relative 'rack_test' - -class AccountFlowTest < RackTest - - setup do - @login = "integration_test_user" - Identity.find_by_address(@login + '@' + APP_CONFIG[:domain]).tap{|i| i.destroy if i} - User.find_by_login(@login).tap{|u| u.destroy if u} - @password = "srp, verify me!" - @srp = SRP::Client.new @login, :password => @password - @user_params = { - :login => @login, - :password_verifier => @srp.verifier.to_s(16), - :password_salt => @srp.salt.to_s(16) - } - post 'http://api.lvh.me:3000/1/users.json', :user => @user_params - @user = User.find_by_login(@login) - end - - teardown do - if @user.reload - @user.identity.destroy - @user.destroy - end - Warden.test_reset! - end - - # this test wraps the api and implements the interface the ruby-srp client. - def handshake(login, aa) - post "http://api.lvh.me:3000/1/sessions.json", - :login => login, - 'A' => aa, - :format => :json - response = JSON.parse(last_response.body) - if response['errors'] - raise RECORD_NOT_FOUND.new(response['errors']) - else - return response['B'] - end - end - - def validate(m) - put "http://api.lvh.me:3000/1/sessions/" + @login + '.json', - :client_auth => m, - :format => :json - return JSON.parse(last_response.body) - end - - test "signup response" do - assert_json_response :login => @login, :ok => true - assert last_response.successful? - end - - test "signup and login with srp via api" do - server_auth = @srp.authenticate(self) - assert last_response.successful? - assert_nil server_auth["errors"] - assert server_auth["M2"] - end - - test "signup and wrong password login attempt" do - srp = SRP::Client.new @login, :password => "wrong password" - server_auth = srp.authenticate(self) - assert_json_error "base" => "Not a valid username/password combination" - assert !last_response.successful? - assert_nil server_auth["M2"] - end - - test "signup and wrong username login attempt" do - srp = SRP::Client.new "wrong_login", :password => @password - server_auth = nil - assert_raises RECORD_NOT_FOUND do - server_auth = srp.authenticate(self) - end - assert_json_error "base" => "Not a valid username/password combination" - assert !last_response.successful? - assert_nil server_auth - end - - test "update password via api" do - @srp.authenticate(self) - @password = "No! Verify me instead." - @srp = SRP::Client.new @login, :password => @password - @user_params = { - # :login => @login, - :password_verifier => @srp.verifier.to_s(16), - :password_salt => @srp.salt.to_s(16) - } - put "http://api.lvh.me:3000/1/users/" + @user.id + '.json', - :user => @user_params, - :format => :json - server_auth = @srp.authenticate(self) - assert last_response.successful? - assert_nil server_auth["errors"] - assert server_auth["M2"] - end - - test "prevent changing login without changing password_verifier" do - server_auth = @srp.authenticate(self) - original_login = @user.login - new_login = 'zaph' - User.find_by_login(new_login).try(:destroy) - Identity.by_address.key(new_login + '@' + APP_CONFIG[:domain]).each do |identity| - identity.destroy - end - put "http://api.lvh.me:3000/1/users/" + @user.id + '.json', :user => {:login => new_login}, :format => :json - assert last_response.successful? - # does not change login if no password_verifier is present - assert_equal original_login, @user.login - end - - test "upload pgp key" do - server_auth = @srp.authenticate(self) - key = FactoryGirl.build :pgp_key - put "http://api.lvh.me:3000/1/users/" + @user.id + '.json', :user => {:public_key => key}, :format => :json - assert_equal key, Identity.for(@user).keys[:pgp] - end - - # eventually probably want to remove most of this into a non-integration - # functional test - test "prevent uploading invalid key" do - server_auth = @srp.authenticate(self) - put "http://api.lvh.me:3000/1/users/" + @user.id + '.json', :user => {:public_key => :blah}, :format => :json - assert_nil Identity.for(@user).keys[:pgp] - end - - test "prevent emptying public key" do - server_auth = @srp.authenticate(self) - key = FactoryGirl.build :pgp_key - put "http://api.lvh.me:3000/1/users/" + @user.id + '.json', :user => {:public_key => key}, :format => :json - put "http://api.lvh.me:3000/1/users/" + @user.id + '.json', :user => {:public_key => ""}, :format => :json - assert_equal key, Identity.for(@user).keys[:pgp] - end - -end diff --git a/users/test/integration/api/login_test.rb b/users/test/integration/api/login_test.rb index fb761e5..92d153f 100644 --- a/users/test/integration/api/login_test.rb +++ b/users/test/integration/api/login_test.rb @@ -1,16 +1,50 @@ require 'test_helper' -require_relative 'rack_test' +require_relative 'srp_test' -class AccountFlowTest < RackTest +class LoginTest < SrpTest setup do - @login = "integration_test_user" + register_user end - test "require json requests" do - put "http://api.lvh.me:3000/1/sessions/" + @login, - :client_auth => "This is not a valid login anyway" + test "requires handshake before validation" do + validate("bla") assert_json_error login: I18n.t(:all_strategies_failed) end + test "login with srp" do + authenticate + assert_equal ["M2", "id", "token"], server_auth.keys + assert last_response.successful? + assert_nil server_auth["errors"] + assert server_auth["M2"] + end + + test "wrong password login attempt" do + authenticate password: "wrong password" + assert_json_error "base" => "Not a valid username/password combination" + assert !last_response.successful? + assert_nil server_auth["M2"] + end + + test "wrong username login attempt" do + assert_raises RECORD_NOT_FOUND do + authenticate login: "wrong login" + end + assert_json_error "base" => "Not a valid username/password combination" + assert !last_response.successful? + assert_nil server_auth + end + + test "logout" do + authenticate + logout + assert_equal 204, last_response.status + end + + test "logout requires token" do + authenticate + logout(nil, {}) + assert_equal 422, last_response.status + end end diff --git a/users/test/integration/api/pgp_key_test.rb b/users/test/integration/api/pgp_key_test.rb new file mode 100644 index 0000000..4c7fb4c --- /dev/null +++ b/users/test/integration/api/pgp_key_test.rb @@ -0,0 +1,35 @@ +require 'test_helper' +require_relative 'srp_test' + +class PgpKeyTest < SrpTest + + setup do + # todo: prepare user and login without doing the srp dance + register_user + authenticate + end + + test "upload pgp key" do + update_user public_key: key + assert_equal key, Identity.for(@user).keys[:pgp] + end + + # eventually probably want to remove most of this into a non-integration + # functional test + test "prevent uploading invalid key" do + update_user public_key: "invalid key" + assert_nil Identity.for(@user).keys[:pgp] + end + + test "prevent emptying public key" do + update_user public_key: key + update_user public_key: "" + assert_equal key, Identity.for(@user).keys[:pgp] + end + + protected + + def key + @key ||= FactoryGirl.build :pgp_key + end +end diff --git a/users/test/integration/api/rack_test.rb b/users/test/integration/api/rack_test.rb deleted file mode 100644 index 9a69f52..0000000 --- a/users/test/integration/api/rack_test.rb +++ /dev/null @@ -1,9 +0,0 @@ -class RackTest < ActiveSupport::TestCase - include Rack::Test::Methods - include Warden::Test::Helpers - include LeapWebCore::AssertResponses - - def app - OUTER_APP - end -end diff --git a/users/test/integration/api/signup_test.rb b/users/test/integration/api/signup_test.rb new file mode 100644 index 0000000..236c547 --- /dev/null +++ b/users/test/integration/api/signup_test.rb @@ -0,0 +1,20 @@ +require 'test_helper' +require_relative 'srp_test' + +class SignupTest < SrpTest + + setup do + register_user + end + + test "signup response" do + assert_json_response :login => @login, :ok => true + assert last_response.successful? + end + + test "signup creates user" do + assert @user + assert_equal @login, @user.login + end +end + diff --git a/users/test/integration/api/srp_test.rb b/users/test/integration/api/srp_test.rb new file mode 100644 index 0000000..946450e --- /dev/null +++ b/users/test/integration/api/srp_test.rb @@ -0,0 +1,104 @@ +class SrpTest < RackTest + + teardown do + if @user + cleanup_user + end + Warden.test_reset! + end + + # this test wraps the api and implements the interface the ruby-srp client. + def handshake(login, aa) + post "http://api.lvh.me:3000/1/sessions.json", + :login => login, + 'A' => aa, + :format => :json + response = JSON.parse(last_response.body) + if response['errors'] + raise RECORD_NOT_FOUND.new(response['errors']) + else + return response['B'] + end + end + + def validate(m) + put "http://api.lvh.me:3000/1/sessions/" + @login + '.json', + :client_auth => m, + :format => :json + return JSON.parse(last_response.body) + end + + protected + + attr_reader :server_auth + + def register_user(login = "integration_test_user", password = 'srp, verify me!') + cleanup_user(login) + post 'http://api.lvh.me:3000/1/users.json', + user_params(login: login, password: password) + @user = User.find_by_login(login) + @login = login + @password = password + end + + def update_user(params) + put "http://api.lvh.me:3000/1/users/" + @user.id + '.json', + user_params(params), + auth_headers + end + + def authenticate(params = nil) + @server_auth = srp(params).authenticate(self) + end + + def auth_headers + return {} if @server_auth.nil? + { + "HTTP_AUTHORIZATION" => encoded_token + } + end + + def encoded_token + ActionController::HttpAuthentication::Token.encode_credentials(server_auth["token"]) + end + + def logout(params=nil, headers=nil) + delete "http://api.lvh.me:3000/1/logout.json", + params || {format: :json}, + headers || auth_headers + end + + def cleanup_user(login = nil) + login ||= @user.login + Identity.by_address.key(login + '@' + APP_CONFIG[:domain]).each do |identity| + identity.destroy + end + if user = User.find_by_login(login) + user.destroy + end + end + + def user_params(params) + if params.keys.include?(:password) + srp_process_password(params) + end + return { user: params, format: :json } + end + + def srp_process_password(params) + params.reverse_merge! login: @login, salt: @salt + @srp = SRP::Client.new params[:login], password: params.delete(:password) + @salt = srp.salt.to_s(16) + params.merge! :password_verifier => srp.verifier.to_s(16), + :password_salt => @salt + end + + def srp(params = nil) + if params.nil? + @srp + else + params.reverse_merge! password: @password + SRP::Client.new(params.delete(:login) || @login, params) + end + end +end diff --git a/users/test/integration/api/update_account_test.rb b/users/test/integration/api/update_account_test.rb new file mode 100644 index 0000000..63429e7 --- /dev/null +++ b/users/test/integration/api/update_account_test.rb @@ -0,0 +1,51 @@ +require 'test_helper' +require_relative 'srp_test' + +class UpdateAccountTest < SrpTest + + setup do + register_user + end + + test "require authentication" do + update_user password: "No! Verify me instead." + assert_access_denied + end + + test "require token" do + authenticate + put "http://api.lvh.me:3000/1/users/" + @user.id + '.json', + user_params(password: "No! Verify me instead.") + assert_access_denied + end + + test "update password via api" do + authenticate + update_user password: "No! Verify me instead." + authenticate + assert last_response.successful? + assert_nil server_auth["errors"] + assert server_auth["M2"] + end + + test "change login with password_verifier" do + authenticate + new_login = 'zaph' + cleanup_user new_login + update_user login: new_login, password: @password + authenticate + assert last_response.successful? + assert_equal new_login, @user.reload.login + end + + test "prevent changing login without changing password_verifier" do + authenticate + original_login = @user.login + new_login = 'zaph' + cleanup_user new_login + update_user login: new_login + assert last_response.successful? + # does not change login if no password_verifier is present + assert_equal original_login, @user.reload.login + end +end diff --git a/users/test/support/integration_test_helper.rb b/users/test/support/integration_test_helper.rb deleted file mode 100644 index 51e47c6..0000000 --- a/users/test/support/integration_test_helper.rb +++ /dev/null @@ -1,12 +0,0 @@ -module IntegrationTestHelper - def submit_signup(username = nil, password = nil) - username ||= "test_#{SecureRandom.urlsafe_base64}".downcase - password ||= SecureRandom.base64 - visit '/users/new' - fill_in 'Username', with: username - fill_in 'Password', with: password - fill_in 'Password confirmation', with: password - click_on 'Sign Up' - return username, password - end -end diff --git a/users/test/unit/unauthorized_user_test.rb b/users/test/unit/unauthenticated_user_test.rb index 5b96ae1..e5fafb8 100644 --- a/users/test/unit/unauthorized_user_test.rb +++ b/users/test/unit/unauthenticated_user_test.rb @@ -1,6 +1,6 @@ require 'test_helper' -class UnauthorizedUserTest < ActiveSupport::TestCase +class UnauthenticatedUserTest < ActiveSupport::TestCase # test "the truth" do # assert true # end |