diff options
| author | elijah <elijah@riseup.net> | 2014-12-02 23:47:35 -0800 | 
|---|---|---|
| committer | elijah <elijah@riseup.net> | 2014-12-02 23:48:11 -0800 | 
| commit | acd6dd2848f38ba1b728f263f297bc6d2ad9e4bb (patch) | |
| tree | 59e4950ce5ab8dcecde717a2d55485833fea5cc7 | |
| parent | 5c300d2399398a7384728ef11b00642b8cfe5128 (diff) | |
add soledad sync test to `leap test`. closes #6431
| -rw-r--r-- | tests/helpers/bonafide_helper.rb | 87 | ||||
| -rwxr-xr-x | tests/helpers/soledad_sync.py | 76 | ||||
| -rw-r--r-- | tests/helpers/srp_helper.rb | 6 | ||||
| -rw-r--r-- | tests/white-box/webapp.rb | 87 | 
4 files changed, 206 insertions, 50 deletions
| diff --git a/tests/helpers/bonafide_helper.rb b/tests/helpers/bonafide_helper.rb new file mode 100644 index 00000000..c84ea142 --- /dev/null +++ b/tests/helpers/bonafide_helper.rb @@ -0,0 +1,87 @@ +# +# helper for the communication with the provider API for creating, authenticating, and deleting accounts. +# + +class LeapTest + +  def assert_tmp_user +    user = assert_create_user +    assert_authenticate_user(user) +    yield user if block_given? +  rescue +    # ^^ ensure here would eat any failed assertions +    assert_delete_user(user) +    raise +  end + +  def api_url(path) +    api = property('api') +    "https://%{domain}:%{port}#{path}" % { +      :domain   => api['domain'], +      :port     => api['port'] +    } +  end + +  # +  # attempts to create a user account via the API, +  # returning the user object if successful. +  # +  def assert_create_user +    user = SRP::User.new +    url = api_url("/1/users.json") +    assert_post(url, user.to_params) do |body| +      assert response = JSON.parse(body), 'response should be JSON' +      assert response['ok'], 'creating a user should be successful' +    end +    user.ok = true +    return user +  end + +  # +  # attempts to authenticate user. if successful, +  # user object is updated with id and session token. +  # +  def assert_authenticate_user(user) +    url = api_url("/1/sessions.json") +    session = SRP::Session.new(user) +    params = {'login' => user.username, 'A' => session.aa} +    assert_post(url, params) do |response, body| +      cookie = response['Set-Cookie'].split(';').first +      assert(response = JSON.parse(body), 'response should be JSON') +      assert(session.bb = response["B"], 'response should include "B"') +      url = api_url("/1/sessions/login.json") +      params = {'client_auth' => session.m, 'A' => session.aa} +      options = {:headers => {'Cookie' => cookie}} +      assert_put(url, params, options) do |body| +        assert(response = JSON.parse(body), 'response should be JSON') +        assert(response['M2'], 'response should include M2') +        user.session_token = response['token'] +        user.id = response['id'] +        assert(user.session_token, 'response should include token') +        assert(user.id, 'response should include user id') +      end +    end +  end + +  # +  # attempts to destroy a user account via the API. +  # +  def assert_delete_user(user) +    if user && user.ok && user.id && user.session_token +      url = api_url("/1/users/#{user.id}.json") +      options = {:headers => { +        "Authorization" => "Token token=\"#{user.session_token}\"" +      }} +      delete(url, {}, options) do |body, response, error| +        if response.code.to_i != 200 +          skip "It appears the web api is too old to support deleting users" +        else +          assert(response = JSON.parse(body), 'response should be JSON') +          assert(response["success"], 'delete should be a success') +          pass +        end +      end +    end +  end + +end diff --git a/tests/helpers/soledad_sync.py b/tests/helpers/soledad_sync.py new file mode 100755 index 00000000..54b6f34b --- /dev/null +++ b/tests/helpers/soledad_sync.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python + +# +# Test Soledad sync +# +# This script performs a slightly modified U1DB sync to the Soledad server and +# returns whether that sync was successful or not. +# +# It takes three arguments: +# +#   uuid   -- uuid of the user to sync +#   token  -- a valid session token +#   server -- the url of the soledad server we should connect to +# +# For example: +# +#   soledad_sync.py f6bef0586fcfdb8705e26a58f2d9e580 uYO-4ucEJFksJ6afjmcYwIyap2vW7bv6uLxk0w_RfCc https://199.119.112.9:2323/user-f6bef0586fcfdb8705e26a58f2d9e580 +# + +import os +import sys +import tempfile +import shutil +import u1db + +from u1db.remote.http_target import HTTPSyncTarget + +# +# monkey patch U1DB's HTTPSyncTarget to perform token based auth +# + +def set_token_credentials(self, uuid, token): +    self._creds = {'token': (uuid, token)} + +def _sign_request(self, method, url_query, params): +    uuid, token = self._creds['token'] +    auth = '%s:%s' % (uuid, token) +    return [('Authorization', 'Token %s' % auth.encode('base64')[:-1])] + +HTTPSyncTarget.set_token_credentials = set_token_credentials +HTTPSyncTarget._sign_request = _sign_request + +# +# Create a temporary local u1db replica and attempt to sync to it. +# Returns a failure message if something went wrong. +# + +def soledad_sync(uuid, token, server): +    tempdir = tempfile.mkdtemp() +    try: +        db = u1db.open(os.path.join(tempdir, '%s.db' % uuid), True) +        creds = {'token': {'uuid': uuid, 'token': token}} +        db.sync(server, creds=creds, autocreate=False) +    finally: +        shutil.rmtree(tempdir) + +# +# exit codes: +# +# 0 - OK +# 1 - WARNING +# 2 - ERROR +# + +if __name__ == '__main__': +    try: +        uuid, token, server = sys.argv[1:] +        result = soledad_sync(uuid, token, server) +        if result is None: +            exit(0) +        else: +            print(result) +            exit(1) +    except Exception as exc: +        print(exc.message or str(exc)) +        exit(2) diff --git a/tests/helpers/srp_helper.rb b/tests/helpers/srp_helper.rb index 9f4d7f5b..0aa1b1fb 100644 --- a/tests/helpers/srp_helper.rb +++ b/tests/helpers/srp_helper.rb @@ -135,16 +135,14 @@ d15dc7d7b46154d6b6ce8ef4ad69b15d4982559b297bcf1885c529f566660e5    class User      include SRP::Util -    attr_accessor :username -    attr_accessor :password -    attr_accessor :salt -    attr_accessor :verifier +    attr_accessor :username, :password, :salt, :verifier, :id, :session_token, :ok      def initialize        @username = "test_user_" + SecureRandom.urlsafe_base64(10).downcase.gsub(/[_-]/, '')        @password = "password_" + SecureRandom.urlsafe_base64(10)        @salt     = bigrand(4).hex        @verifier = modpow(GENERATOR, private_key) +      @ok       = false      end      def private_key diff --git a/tests/white-box/webapp.rb b/tests/white-box/webapp.rb index 7fbab1af..d2419ab4 100644 --- a/tests/white-box/webapp.rb +++ b/tests/white-box/webapp.rb @@ -42,50 +42,23 @@ class Webapp < LeapTest    end    def test_05_Can_create_and_authenticate_and_delete_user_via_API? -    @user = SRP::User.new -    @session_token = nil -    @user_id = nil - -    # create user -    url = api_url("/1/users.json") -    assert_post(url, @user.to_params) do |body| -      assert response = JSON.parse(body), 'response should be JSON' -      assert response['ok'], 'creating a user should be successful' -    end - -    # authenticate -    url = api_url("/1/sessions.json") -    session = SRP::Session.new(@user) -    params = {'login' => @user.username, 'A' => session.aa} -    assert_post(url, params) do |response, body| -      cookie = response['Set-Cookie'].split(';').first -      assert(response = JSON.parse(body), 'response should be JSON') -      assert(bb = response["B"]) -      session.bb = bb -      url = api_url("/1/sessions/login.json") -      params = {'client_auth' => session.m, 'A' => session.aa} -      options = {:headers => {'Cookie' => cookie}} -      assert_put(url, params, options) do |body| -        assert(response = JSON.parse(body), 'response should be JSON') -        assert(response['M2'], 'response should include M2') -        assert(@session_token = response['token'], 'response should include token') -        assert(@user_id = response['id'], 'response should include user id') -      end -    end +    assert_tmp_user +    pass +  end -    # delete -    url = api_url("/1/users/#{@user_id}.json") -    options = {:headers => { -      "Authorization" => "Token token=\"#{@session_token}\"" -    }} -    delete(url, {}, options) do |body, response, error| -      if response.code.to_i != 200 -        skip "It appears the web api is too old to support deleting users" -      else -        assert(response = JSON.parse(body), 'response should be JSON') -        assert(response["success"], 'delete should be a success') +  def test_06_Can_sync_Soledad? +    soledad_config = property('definition_files.soledad_service') +    if soledad_config && !soledad_config.empty? +      soledad_server = pick_soledad_server(soledad_config) +      assert_tmp_user do |user| +        assert user_db_exists?(user), "Could not find user db for test user #{user.username}" +        command = File.expand_path "../../helpers/soledad_sync.py", __FILE__ +        soledad_url = "https://#{soledad_server}/user-#{user.id}" +        assert_run "#{command} #{user.id} #{user.session_token} #{soledad_url}"          pass        end +    else +      skip 'No soledad service configuration'      end    end @@ -98,11 +71,33 @@ class Webapp < LeapTest      }    end -  def api_url(path) -    "https://%{domain}:%{port}#{path}" % { -      :domain   => property('api.domain'), -      :port     => property('api.port') -    } +  # +  # pick a random soledad server. +  # I am not sure why, but using IP address directly does not work. +  # +  def pick_soledad_server(soledad_config_json_str) +    soledad_config = JSON.parse(soledad_config_json_str) +    host_name = soledad_config['hosts'].keys.shuffle.first +    hostname = soledad_config['hosts'][host_name]['hostname'] +    port = soledad_config['hosts'][host_name]['port'] +    return "#{hostname}:#{port}" +  end + +  # +  # returns true if the per-user db created by tapicero exists. +  # we try three times, and give up after that. +  # +  def user_db_exists?(user) +    3.times do +      sleep 0.1 +      get(couchdb_url("/user-#{user.id}/_design/docs")) do |body, response, error| +        if response.code.to_i == 200 +          return true +        end +      end +      sleep 0.2 +    end +    return false    end    # | 
