From b53bd889d5407fb357329b414bcae929176c0690 Mon Sep 17 00:00:00 2001 From: ankonym Date: Wed, 7 Oct 2015 11:19:26 +0200 Subject: Change webapp tests to work with enabled invite codes This will generate an invite code so test_05_Can_create_and_authenticate_and_delete_user_via_API? will work correctly (when invite codes are required for signups). --- tests/helpers/bonafide_helper.rb | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'tests/helpers') diff --git a/tests/helpers/bonafide_helper.rb b/tests/helpers/bonafide_helper.rb index 9b26eaaf..39f761fe 100644 --- a/tests/helpers/bonafide_helper.rb +++ b/tests/helpers/bonafide_helper.rb @@ -32,7 +32,11 @@ class LeapTest def assert_create_user user = SRP::User.new url = api_url("/1/users.json") - assert_post(url, user.to_params) do |body| + + params = user.to_params + params['user[invite_code]'] = generate_invite_code + + assert_post(url, params) do |body| assert response = JSON.parse(body), 'response should be JSON' assert response['ok'], "Creating a user should be successful, got #{response.inspect} instead." end @@ -40,6 +44,13 @@ class LeapTest return user end + def generate_invite_code + if property('webapp.invite_required') + `cd /srv/leap/webapp/ && sudo RAILS_ENV=production bundle exec rake generate_invites[1]`.gsub(/\n/, "") + end + end + + # # attempts to authenticate user. if successful, # user object is updated with id and session token. -- cgit v1.2.3 From dc426cc777aec921772c1d8c2fa014d61aa90d33 Mon Sep 17 00:00:00 2001 From: ankonym Date: Wed, 7 Oct 2015 12:11:21 +0200 Subject: Modify bonafide_helper to improve user creation test with invites Will now use the correct user to generate invite codes and only add invite code parameter when invite codes are enabled --- tests/helpers/bonafide_helper.rb | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'tests/helpers') diff --git a/tests/helpers/bonafide_helper.rb b/tests/helpers/bonafide_helper.rb index 39f761fe..1a6dc6fe 100644 --- a/tests/helpers/bonafide_helper.rb +++ b/tests/helpers/bonafide_helper.rb @@ -34,7 +34,10 @@ class LeapTest url = api_url("/1/users.json") params = user.to_params - params['user[invite_code]'] = generate_invite_code + + if property('webapp.invite_required') + params['user[invite_code]'] = generate_invite_code + end assert_post(url, params) do |body| assert response = JSON.parse(body), 'response should be JSON' @@ -45,9 +48,7 @@ class LeapTest end def generate_invite_code - if property('webapp.invite_required') - `cd /srv/leap/webapp/ && sudo RAILS_ENV=production bundle exec rake generate_invites[1]`.gsub(/\n/, "") - end + `cd /srv/leap/webapp/ && sudo -u leap-webapp RAILS_ENV=production bundle exec rake generate_invites[1]`.gsub(/\n/, "") end -- cgit v1.2.3 From 2053883c81d261b5a9af5011bd6f0e8bc709f8d1 Mon Sep 17 00:00:00 2001 From: ankonym Date: Mon, 12 Oct 2015 11:19:21 +0200 Subject: Fix soledad test when invite codes are enabled This provides an invite code when invite codes are enabled while the test runs (but it does not get deleted yet afterwards) --- tests/helpers/bonafide_helper.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'tests/helpers') diff --git a/tests/helpers/bonafide_helper.rb b/tests/helpers/bonafide_helper.rb index 1a6dc6fe..82db3973 100644 --- a/tests/helpers/bonafide_helper.rb +++ b/tests/helpers/bonafide_helper.rb @@ -36,7 +36,8 @@ class LeapTest params = user.to_params if property('webapp.invite_required') - params['user[invite_code]'] = generate_invite_code + @invite_code = generate_invite_code + params['user[invite_code]'] = @invite_code end assert_post(url, params) do |body| -- cgit v1.2.3 From 1b1b9fa708ea93635f341c086e264b01e3717a67 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Fri, 30 Oct 2015 17:28:55 -0400 Subject: new soledad-sync test this new test uses leap.soledad.client to perform an actual sync against the soledad server. closes: #7523 --- tests/helpers/client_side_db.py | 268 ++++++++++++++++++++++++++++++++++++++++ tests/helpers/soledad_sync.py | 123 +++++++++--------- 2 files changed, 328 insertions(+), 63 deletions(-) create mode 100644 tests/helpers/client_side_db.py (limited to 'tests/helpers') diff --git a/tests/helpers/client_side_db.py b/tests/helpers/client_side_db.py new file mode 100644 index 00000000..f52354cc --- /dev/null +++ b/tests/helpers/client_side_db.py @@ -0,0 +1,268 @@ +import logging +import os +import argparse +import tempfile +import getpass +import requests +import srp._pysrp as srp +import binascii +import logging +import json +import time + +from twisted.internet import reactor +from twisted.internet.defer import inlineCallbacks + +from leap.soledad.client import Soledad +from leap.keymanager import KeyManager +from leap.keymanager.openpgp import OpenPGPKey + + +""" +Helper functions to give access to client-side Soledad database. +""" + + +# create a logger +logger = logging.getLogger(__name__) + +# DEBUG: enable debug logs +# LOG_FORMAT = '%(asctime)s %(message)s' +# logging.basicConfig(format=LOG_FORMAT, level=logging.DEBUG) + + +safe_unhexlify = lambda x: binascii.unhexlify(x) if ( + len(x) % 2 == 0) else binascii.unhexlify('0' + x) + + +def _fail(reason): + logger.error('Fail: ' + reason) + exit(2) + + +def _get_api_info(provider): + info = requests.get( + 'https://'+provider+'/provider.json', verify=False).json() + return info['api_uri'], info['api_version'] + + +def _login(username, passphrase, provider, api_uri, api_version): + usr = srp.User(username, passphrase, srp.SHA256, srp.NG_1024) + auth = None + try: + auth = _authenticate(api_uri, api_version, usr).json() + except requests.exceptions.ConnectionError: + _fail('Could not connect to server.') + if 'errors' in auth: + _fail(str(auth['errors'])) + return api_uri, api_version, auth + + +def _authenticate(api_uri, api_version, usr): + api_url = "%s/%s" % (api_uri, api_version) + session = requests.session() + uname, A = usr.start_authentication() + params = {'login': uname, 'A': binascii.hexlify(A)} + init = session.post( + api_url + '/sessions', data=params, verify=False).json() + if 'errors' in init: + _fail('test user not found') + M = usr.process_challenge( + safe_unhexlify(init['salt']), safe_unhexlify(init['B'])) + return session.put(api_url + '/sessions/' + uname, verify=False, + data={'client_auth': binascii.hexlify(M)}) + + +def _get_soledad_info(username, provider, passphrase, basedir): + api_uri, api_version = _get_api_info(provider) + auth = _login(username, passphrase, provider, api_uri, api_version) + # get soledad server url + service_url = '%s/%s/config/soledad-service.json' % \ + (api_uri, api_version) + soledad_hosts = requests.get(service_url, verify=False).json()['hosts'] + hostnames = soledad_hosts.keys() + # allow for choosing the host + host = hostnames[0] + if len(hostnames) > 1: + i = 1 + print "There are many available hosts:" + for h in hostnames: + print " (%d) %s.%s" % (i, h, provider) + i += 1 + choice = raw_input("Choose a host to use (default: 1): ") + if choice != '': + host = hostnames[int(choice) - 1] + server_url = 'https://%s:%d/user-%s' % \ + (soledad_hosts[host]['hostname'], soledad_hosts[host]['port'], + auth[2]['id']) + # get provider ca certificate + ca_cert = requests.get('https://%s/ca.crt' % provider, verify=False).text + cert_file = os.path.join(basedir, 'ca.crt') + with open(cert_file, 'w') as f: + f.write(ca_cert) + return auth[2]['id'], server_url, cert_file, auth[2]['token'] + + +def get_soledad_instance(uuid, passphrase, basedir, server_url, cert_file, + token): + # setup soledad info + logger.info('UUID is %s' % uuid) + logger.info('Server URL is %s' % server_url) + secrets_path = os.path.join( + basedir, '%s.secret' % uuid) + local_db_path = os.path.join( + basedir, '%s.db' % uuid) + # instantiate soledad + return Soledad( + uuid, + unicode(passphrase), + secrets_path=secrets_path, + local_db_path=local_db_path, + server_url=server_url, + cert_file=cert_file, + auth_token=token, + defer_encryption=True) + + +def _get_keymanager_instance(username, provider, soledad, token, + ca_cert_path=None, api_uri=None, api_version=None, + uid=None, gpgbinary=None): + return KeyManager( + "{username}@{provider}".format(username=username, provider=provider), + "http://uri", + soledad, + token=token, + ca_cert_path=ca_cert_path, + api_uri=api_uri, + api_version=api_version, + uid=uid, + gpgbinary=gpgbinary) + + + +def _get_passphrase(args): + passphrase = args.passphrase + if passphrase is None: + passphrase = getpass.getpass( + 'Password for %s@%s: ' % (args.username, args.provider)) + return passphrase + + +def _get_basedir(args): + basedir = args.basedir + if basedir is None: + basedir = tempfile.mkdtemp() + elif not os.path.isdir(basedir): + os.mkdir(basedir) + logger.info('Using %s as base directory.' % basedir) + return basedir + + +@inlineCallbacks +def _export_key(args, km, fname, private=False): + address = args.username + "@" + args.provider + pkey = yield km.get_key( + address, OpenPGPKey, private=private, fetch_remote=False) + with open(args.export_private_key, "w") as f: + f.write(pkey.key_data) + + +@inlineCallbacks +def _export_incoming_messages(soledad, directory): + yield soledad.create_index("by-incoming", "bool(incoming)") + docs = yield soledad.get_from_index("by-incoming", '1') + i = 1 + for doc in docs: + with open(os.path.join(directory, "message_%d.gpg" % i), "w") as f: + f.write(doc.content["_enc_json"]) + i += 1 + + +@inlineCallbacks +def _get_all_docs(soledad): + _, docs = yield soledad.get_all_docs() + for doc in docs: + print json.dumps(doc.content, indent=4) + + +# main program + +@inlineCallbacks +def _main(soledad, km, args): + try: + if args.create_docs: + for i in xrange(args.create_docs): + t = time.time() + logger.debug( + "Creating doc %d/%d..." % (i + 1, args.create_docs)) + content = { + 'datetime': time.strftime( + "%Y-%m-%d %H:%M:%S", time.gmtime(t)), + 'timestamp': t, + 'index': i, + 'total': args.create_docs, + } + yield soledad.create_doc(content) + if args.sync: + yield soledad.sync() + if args.repeat_sync: + old_gen = 0 + new_gen = yield soledad.sync() + while old_gen != new_gen: + old_gen = new_gen + new_gen = yield soledad.sync() + if args.get_all_docs: + yield _get_all_docs(soledad) + if args.export_private_key: + yield _export_key(args, km, args.export_private_key, private=True) + if args.export_public_key: + yield _export_key(args, km, args.expoert_public_key, private=False) + if args.export_incoming_messages: + yield _export_incoming_messages( + soledad, args.export_incoming_messages) + except Exception as e: + logger.error(e) + finally: + soledad.close() + reactor.callWhenRunning(reactor.stop) + + +if __name__ == '__main__': + args = _parse_args() + passphrase = _get_passphrase(args) + basedir = _get_basedir(args) + + if not args.use_auth_data: + # get auth data from server + uuid, server_url, cert_file, token = \ + _get_soledad_info( + args.username, args.provider, passphrase, basedir) + else: + # load auth data from file + with open(args.use_auth_data) as f: + auth_data = json.loads(f.read()) + uuid = auth_data['uuid'] + server_url = auth_data['server_url'] + cert_file = auth_data['cert_file'] + token = auth_data['token'] + + # export auth data to a file + if args.export_auth_data: + with open(args.export_auth_data, "w") as f: + f.write(json.dumps({ + 'uuid': uuid, + 'server_url': server_url, + 'cert_file': cert_file, + 'token': token, + })) + + soledad = _get_soledad_instance( + uuid, passphrase, basedir, server_url, cert_file, token) + km = _get_keymanager_instance( + args.username, + args.provider, + soledad, + token, + uid=uuid) + _main(soledad, km, args) + reactor.run() diff --git a/tests/helpers/soledad_sync.py b/tests/helpers/soledad_sync.py index 2fb865fc..a1cea069 100755 --- a/tests/helpers/soledad_sync.py +++ b/tests/helpers/soledad_sync.py @@ -1,78 +1,75 @@ #!/usr/bin/env python +""" +soledad_sync.py -# -# 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 -# +This script exercises soledad synchronization. +Its exit code is 0 if the sync took place correctly, 1 otherwise. +It takes 5 arguments: + + uuid: uuid of the user to sync + token: a valid session token + server: the url of the soledad server we should connect to + cert_file: the file containing the certificate for the CA that signed the + cert for the soledad server. + password: the password for the user to sync + +__author__: kali@leap.se +""" import os import sys -import traceback import tempfile -import shutil -import u1db -from u1db.remote.http_target import HTTPSyncTarget +# This is needed because the twisted shipped with wheezy is too old +# to do proper ssl verification. +os.environ['SKIP_TWISTED_SSL_CHECK'] = '1' -# -# monkey patch U1DB's HTTPSyncTarget to perform token based auth -# +from twisted.internet import defer, reactor -def set_token_credentials(self, uuid, token): - self._creds = {'token': (uuid, token)} +from client_side_db import get_soledad_instance +from leap.common.events import flags -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])] +flags.set_events_enabled(False) -HTTPSyncTarget.set_token_credentials = set_token_credentials -HTTPSyncTarget._sign_request = _sign_request +NUMDOCS = 1 +USAGE = "Usage: %s uuid token server cert_file password" % sys.argv[0] -# -# 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 -# +def bail(msg, exitcode): + print "[!] %s" % msg + sys.exit(exitcode) + + +def create_docs(soledad): + """ + Populates the soledad database with dummy messages, so we can exercise + sending payloads during the sync. + """ + deferreds = [] + for index in xrange(NUMDOCS): + deferreds.append(soledad.create_doc({'payload': 'dummy'})) + return defer.gatherResults(deferreds) + +# main program 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)) - traceback.print_exc(file=sys.stdout) - exit(2) + + tempdir = tempfile.mkdtemp() + if len(sys.argv) < 6: + bail(USAGE, 2) + uuid, token, server, cert_file, passphrase = sys.argv[1:] + s = get_soledad_instance( + uuid, passphrase, tempdir, server, cert_file, token) + + def onSyncDone(sync_result): + print "SYNC_RESULT:", sync_result + s.close() + reactor.stop() + + def start_sync(): + d = create_docs(s) + d.addCallback(lambda _: s.sync()) + d.addCallback(onSyncDone) + + reactor.callWhenRunning(start_sync) + reactor.run() -- cgit v1.2.3 From 6d9dc1a9bed8da7827e6f7cf80fe2ecff49ca308 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Fri, 13 Nov 2015 12:06:18 -0400 Subject: remove keymanager --- tests/helpers/client_side_db.py | 107 ---------------------------------------- 1 file changed, 107 deletions(-) (limited to 'tests/helpers') diff --git a/tests/helpers/client_side_db.py b/tests/helpers/client_side_db.py index f52354cc..d2a4bbcb 100644 --- a/tests/helpers/client_side_db.py +++ b/tests/helpers/client_side_db.py @@ -1,28 +1,21 @@ import logging import os -import argparse import tempfile import getpass import requests import srp._pysrp as srp import binascii -import logging import json -import time -from twisted.internet import reactor from twisted.internet.defer import inlineCallbacks from leap.soledad.client import Soledad -from leap.keymanager import KeyManager -from leap.keymanager.openpgp import OpenPGPKey """ Helper functions to give access to client-side Soledad database. """ - # create a logger logger = logging.getLogger(__name__) @@ -123,23 +116,6 @@ def get_soledad_instance(uuid, passphrase, basedir, server_url, cert_file, auth_token=token, defer_encryption=True) - -def _get_keymanager_instance(username, provider, soledad, token, - ca_cert_path=None, api_uri=None, api_version=None, - uid=None, gpgbinary=None): - return KeyManager( - "{username}@{provider}".format(username=username, provider=provider), - "http://uri", - soledad, - token=token, - ca_cert_path=ca_cert_path, - api_uri=api_uri, - api_version=api_version, - uid=uid, - gpgbinary=gpgbinary) - - - def _get_passphrase(args): passphrase = args.passphrase if passphrase is None: @@ -183,86 +159,3 @@ def _get_all_docs(soledad): _, docs = yield soledad.get_all_docs() for doc in docs: print json.dumps(doc.content, indent=4) - - -# main program - -@inlineCallbacks -def _main(soledad, km, args): - try: - if args.create_docs: - for i in xrange(args.create_docs): - t = time.time() - logger.debug( - "Creating doc %d/%d..." % (i + 1, args.create_docs)) - content = { - 'datetime': time.strftime( - "%Y-%m-%d %H:%M:%S", time.gmtime(t)), - 'timestamp': t, - 'index': i, - 'total': args.create_docs, - } - yield soledad.create_doc(content) - if args.sync: - yield soledad.sync() - if args.repeat_sync: - old_gen = 0 - new_gen = yield soledad.sync() - while old_gen != new_gen: - old_gen = new_gen - new_gen = yield soledad.sync() - if args.get_all_docs: - yield _get_all_docs(soledad) - if args.export_private_key: - yield _export_key(args, km, args.export_private_key, private=True) - if args.export_public_key: - yield _export_key(args, km, args.expoert_public_key, private=False) - if args.export_incoming_messages: - yield _export_incoming_messages( - soledad, args.export_incoming_messages) - except Exception as e: - logger.error(e) - finally: - soledad.close() - reactor.callWhenRunning(reactor.stop) - - -if __name__ == '__main__': - args = _parse_args() - passphrase = _get_passphrase(args) - basedir = _get_basedir(args) - - if not args.use_auth_data: - # get auth data from server - uuid, server_url, cert_file, token = \ - _get_soledad_info( - args.username, args.provider, passphrase, basedir) - else: - # load auth data from file - with open(args.use_auth_data) as f: - auth_data = json.loads(f.read()) - uuid = auth_data['uuid'] - server_url = auth_data['server_url'] - cert_file = auth_data['cert_file'] - token = auth_data['token'] - - # export auth data to a file - if args.export_auth_data: - with open(args.export_auth_data, "w") as f: - f.write(json.dumps({ - 'uuid': uuid, - 'server_url': server_url, - 'cert_file': cert_file, - 'token': token, - })) - - soledad = _get_soledad_instance( - uuid, passphrase, basedir, server_url, cert_file, token) - km = _get_keymanager_instance( - args.username, - args.provider, - soledad, - token, - uid=uuid) - _main(soledad, km, args) - reactor.run() -- cgit v1.2.3 From 675f826d4a85c1cd26e30d0f8ef52d5b592c0d1c Mon Sep 17 00:00:00 2001 From: varac Date: Fri, 6 Nov 2015 00:16:24 +0100 Subject: [bug] [jessie] check for 1 stunnel instance only - Resolves: #7574 --- tests/helpers/os_helper.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'tests/helpers') diff --git a/tests/helpers/os_helper.rb b/tests/helpers/os_helper.rb index aad67dda..c57300e2 100644 --- a/tests/helpers/os_helper.rb +++ b/tests/helpers/os_helper.rb @@ -9,7 +9,10 @@ class LeapTest output.each_line.map{|line| pid = line.split(' ')[0] process = line.gsub(/(#{pid} |\n)/, '') - if process =~ /pgrep --full --list-name/ + # filter out pgrep cmd itself + # on wheezy hosts, the "process" var contains the whole cmd including all parameters + # on jessie hosts, it only contains the first cmd (which is the default sheel invoked by 'sh') + if process =~ /^sh/ nil else {:pid => pid, :process => process} @@ -35,4 +38,4 @@ class LeapTest end end -end \ No newline at end of file +end -- cgit v1.2.3 From 75341eeb905e9c7312232d8c9c4077a358b8cb20 Mon Sep 17 00:00:00 2001 From: varac Date: Fri, 6 Nov 2015 00:16:24 +0100 Subject: [bug] [jessie] check for 1 stunnel instance only - Resolves: #7574 --- tests/helpers/os_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tests/helpers') diff --git a/tests/helpers/os_helper.rb b/tests/helpers/os_helper.rb index c57300e2..da9ac843 100644 --- a/tests/helpers/os_helper.rb +++ b/tests/helpers/os_helper.rb @@ -38,4 +38,4 @@ class LeapTest end end -end +end \ No newline at end of file -- cgit v1.2.3 From 9f12fd1b52ad79873a699b9d3faa29ff3e3c2677 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Thu, 19 Nov 2015 08:44:56 -0400 Subject: [bug] do not fail if no requests/srp dependencies present client_side_db helper has some extra functions that can be useful in the future for further tests, but right now it shouldn't depend on requests, since it already get all the relevant soledad info passed as arguments. for the same reason, it doesn't need to depend on srp, since we pass the token. --- tests/helpers/client_side_db.py | 50 +++++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 22 deletions(-) (limited to 'tests/helpers') diff --git a/tests/helpers/client_side_db.py b/tests/helpers/client_side_db.py index d2a4bbcb..2f8c220f 100644 --- a/tests/helpers/client_side_db.py +++ b/tests/helpers/client_side_db.py @@ -2,11 +2,15 @@ import logging import os import tempfile import getpass -import requests -import srp._pysrp as srp import binascii import json +try: + import requests + import srp._pysrp as srp +except ImportError: + pass + from twisted.internet.defer import inlineCallbacks from leap.soledad.client import Soledad @@ -14,6 +18,7 @@ from leap.soledad.client import Soledad """ Helper functions to give access to client-side Soledad database. +Copied over from soledad/scripts folder. """ # create a logger @@ -33,6 +38,27 @@ def _fail(reason): exit(2) +def get_soledad_instance(uuid, passphrase, basedir, server_url, cert_file, + token): + # setup soledad info + logger.info('UUID is %s' % uuid) + logger.info('Server URL is %s' % server_url) + secrets_path = os.path.join( + basedir, '%s.secret' % uuid) + local_db_path = os.path.join( + basedir, '%s.db' % uuid) + # instantiate soledad + return Soledad( + uuid, + unicode(passphrase), + secrets_path=secrets_path, + local_db_path=local_db_path, + server_url=server_url, + cert_file=cert_file, + auth_token=token, + defer_encryption=True) + + def _get_api_info(provider): info = requests.get( 'https://'+provider+'/provider.json', verify=False).json() @@ -96,26 +122,6 @@ def _get_soledad_info(username, provider, passphrase, basedir): return auth[2]['id'], server_url, cert_file, auth[2]['token'] -def get_soledad_instance(uuid, passphrase, basedir, server_url, cert_file, - token): - # setup soledad info - logger.info('UUID is %s' % uuid) - logger.info('Server URL is %s' % server_url) - secrets_path = os.path.join( - basedir, '%s.secret' % uuid) - local_db_path = os.path.join( - basedir, '%s.db' % uuid) - # instantiate soledad - return Soledad( - uuid, - unicode(passphrase), - secrets_path=secrets_path, - local_db_path=local_db_path, - server_url=server_url, - cert_file=cert_file, - auth_token=token, - defer_encryption=True) - def _get_passphrase(args): passphrase = args.passphrase if passphrase is None: -- cgit v1.2.3 From d610405e585966406fd9813423e300a39ef300b5 Mon Sep 17 00:00:00 2001 From: Christoph Kluenter Date: Tue, 1 Dec 2015 15:02:02 +0100 Subject: don't check in database if user was deleted. we already check if the webapp returned success when it deleted the user. If the webapp had failed it would not have returned success. leap_web has tests that prove this. This fixes https://leap.se/code/issues/7625 --- tests/helpers/bonafide_helper.rb | 8 -------- 1 file changed, 8 deletions(-) (limited to 'tests/helpers') diff --git a/tests/helpers/bonafide_helper.rb b/tests/helpers/bonafide_helper.rb index 82db3973..20c3ca8d 100644 --- a/tests/helpers/bonafide_helper.rb +++ b/tests/helpers/bonafide_helper.rb @@ -98,14 +98,6 @@ class LeapTest assert(response = JSON.parse(body), 'Delete response should be JSON') assert(response["success"], 'Deleting user should be a success') end - domain = property('domain.full_suffix') - identities_url = couchdb_url("/identities/_design/Identity/_view/by_address?key=%22#{user.username}@#{domain}%22") - get(identities_url) do |body, response, error| - assert error.nil?, "Error checking identities db: #{error}" - assert response.code.to_i == 200, "Unable to check that user identity was deleted: HTTP response from API should have code 200, was #{response.code} #{error} #{body}" - assert(response = JSON.parse(body), 'Couch response should be JSON') - assert response['rows'].empty?, "Identity should have been deleted for test user #{user.username} (id #{user.id}), but was not! Response was: #{body}." - end end end -- cgit v1.2.3 From c010d7dd1307e5cdb344b25fea078af8108df20a Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Mon, 25 Jan 2016 12:01:39 -0400 Subject: [bug] add errback to log failure --- tests/helpers/soledad_sync.py | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'tests/helpers') diff --git a/tests/helpers/soledad_sync.py b/tests/helpers/soledad_sync.py index a1cea069..b8e8fbc9 100755 --- a/tests/helpers/soledad_sync.py +++ b/tests/helpers/soledad_sync.py @@ -25,6 +25,7 @@ import tempfile os.environ['SKIP_TWISTED_SSL_CHECK'] = '1' from twisted.internet import defer, reactor +from twisted.python import log from client_side_db import get_soledad_instance from leap.common.events import flags @@ -66,10 +67,15 @@ if __name__ == '__main__': s.close() reactor.stop() + def log_and_exit(f): + log.err(f) + reactor.stop() + def start_sync(): d = create_docs(s) d.addCallback(lambda _: s.sync()) d.addCallback(onSyncDone) + d.addErrback(log_and_exit) reactor.callWhenRunning(start_sync) reactor.run() -- cgit v1.2.3 From 2599431bb42380f5d8d45769c9a56175f8137267 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Thu, 25 Feb 2016 11:59:39 -0400 Subject: [bug] remove tempdir after soledad sync test Closes: #7921 --- tests/helpers/soledad_sync.py | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'tests/helpers') diff --git a/tests/helpers/soledad_sync.py b/tests/helpers/soledad_sync.py index b8e8fbc9..f4fc81ae 100755 --- a/tests/helpers/soledad_sync.py +++ b/tests/helpers/soledad_sync.py @@ -17,6 +17,7 @@ It takes 5 arguments: __author__: kali@leap.se """ import os +import shutil import sys import tempfile @@ -56,8 +57,13 @@ def create_docs(soledad): if __name__ == '__main__': tempdir = tempfile.mkdtemp() + + def rm_tempdir(): + shutil.rmtree(tempdir) + if len(sys.argv) < 6: bail(USAGE, 2) + uuid, token, server, cert_file, passphrase = sys.argv[1:] s = get_soledad_instance( uuid, passphrase, tempdir, server, cert_file, token) @@ -65,10 +71,12 @@ if __name__ == '__main__': def onSyncDone(sync_result): print "SYNC_RESULT:", sync_result s.close() + rm_tempdir() reactor.stop() def log_and_exit(f): log.err(f) + rm_tempdir() reactor.stop() def start_sync(): -- cgit v1.2.3 From 61441a85b2a96cedd33c7bdc5940f0b8858de7be Mon Sep 17 00:00:00 2001 From: elijah Date: Thu, 17 Mar 2016 16:53:33 -0700 Subject: mx test: query identities table for a random record. closes #6406 --- tests/helpers/couchdb_helper.rb | 1 + tests/helpers/http_helper.rb | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'tests/helpers') diff --git a/tests/helpers/couchdb_helper.rb b/tests/helpers/couchdb_helper.rb index d4d3c0e0..312e38ac 100644 --- a/tests/helpers/couchdb_helper.rb +++ b/tests/helpers/couchdb_helper.rb @@ -66,6 +66,7 @@ class LeapTest # port: 5984 # def couchdb_url_via_localhost(path="", options=nil) + path = path.gsub('"', '%22') port = (options && options[:port]) || assert_property('couch.port') if options && options[:username] password = property("couch.users.%{username}.password" % options) diff --git a/tests/helpers/http_helper.rb b/tests/helpers/http_helper.rb index 0b13b754..2fcdc910 100644 --- a/tests/helpers/http_helper.rb +++ b/tests/helpers/http_helper.rb @@ -87,12 +87,13 @@ class LeapTest options ||= {} error_msg = options[:error_msg] || (url.respond_to?(:memo) ? url.memo : nil) http_send(method, url, params, options) do |body, response, error| - if body && response && response.code.to_i >= 200 && response.code.to_i < 300 + ok = response && response.code.to_i >= 200 && response.code.to_i < 300 + if body && ok if block yield(body) if block.arity == 1 yield(response, body) if block.arity == 2 end - elsif response + elsif response && !ok fail ["Expected a 200 status code from #{method} #{url}, but got #{response.code} instead.", error_msg, body].compact.join("\n") else fail ["Expected a response from #{method} #{url}, but got \"#{error}\" instead.", error_msg, body].compact.join("\n"), error -- cgit v1.2.3 From 966120293ec289636874edda6089b99dc49cb9ae Mon Sep 17 00:00:00 2001 From: elijah Date: Fri, 18 Mar 2016 09:40:41 -0700 Subject: tests: fix mx test, ensure password is redacted. --- tests/helpers/couchdb_helper.rb | 2 ++ 1 file changed, 2 insertions(+) (limited to 'tests/helpers') diff --git a/tests/helpers/couchdb_helper.rb b/tests/helpers/couchdb_helper.rb index 312e38ac..27dabfcb 100644 --- a/tests/helpers/couchdb_helper.rb +++ b/tests/helpers/couchdb_helper.rb @@ -15,6 +15,7 @@ class LeapTest # connect_port: 15984 # def couchdb_urls_via_stunnel(path="", options=nil) + path = path.gsub('"', '%22') if options && options[:username] && options[:password] userpart = "%{username}:%{password}@" % options else @@ -46,6 +47,7 @@ class LeapTest # writable: true # def couchdb_url_via_haproxy(path="", options=nil) + path = path.gsub('"', '%22') if options && options[:username] && options[:password] userpart = "%{username}:%{password}@" % options else -- cgit v1.2.3 From eac3056c237d523f4786593922fe8f88eb65dff7 Mon Sep 17 00:00:00 2001 From: elijah Date: Tue, 29 Mar 2016 13:27:01 -0700 Subject: testing: adds mx delivery tests --- tests/helpers/bonafide_helper.rb | 190 ++++++++++++++++++++++++++++++++------- tests/helpers/couchdb_helper.rb | 36 ++++++++ tests/helpers/http_helper.rb | 27 ++++-- tests/helpers/smtp_helper.rb | 45 ++++++++++ tests/helpers/srp_helper.rb | 4 +- 5 files changed, 260 insertions(+), 42 deletions(-) create mode 100644 tests/helpers/smtp_helper.rb (limited to 'tests/helpers') diff --git a/tests/helpers/bonafide_helper.rb b/tests/helpers/bonafide_helper.rb index 20c3ca8d..95e3e3b7 100644 --- a/tests/helpers/bonafide_helper.rb +++ b/tests/helpers/bonafide_helper.rb @@ -17,58 +17,53 @@ class LeapTest raise exc 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") - + def assert_create_user(username=nil, auth=nil) + user = SRP::User.new(username) + url = api_url("/users.json") params = user.to_params - - if property('webapp.invite_required') - @invite_code = generate_invite_code - params['user[invite_code]'] = @invite_code + if auth + options = api_options(:auth => auth) + else + options = api_options + if property('webapp.invite_required') + @invite_code = generate_invite_code + params['user[invite_code]'] = @invite_code + end end - assert_post(url, params) do |body| + assert_post(url, params, options) do |body| assert response = JSON.parse(body), 'response should be JSON' assert response['ok'], "Creating a user should be successful, got #{response.inspect} instead." + user.ok = true + user.id = response['id'] end - user.ok = true return user end + # TODO: use the api for this instead. def generate_invite_code `cd /srv/leap/webapp/ && sudo -u leap-webapp RAILS_ENV=production bundle exec rake generate_invites[1]`.gsub(/\n/, "") 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") + url = api_url("/sessions.json") session = SRP::Session.new(user) params = {'login' => user.username, 'A' => session.aa} - assert_post(url, params) do |response, body| + assert_post(url, params, api_options) do |body, response| 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") + url = api_url("/sessions/login.json") params = {'client_auth' => session.m, 'A' => session.aa} - options = {:headers => {'Cookie' => cookie}} - assert_put(url, params, options) do |body| + assert_put(url, params, api_options('Cookie' => cookie)) do |body| assert(response = JSON.parse(body), 'response should be JSON') assert(response['M2'], 'response should include M2') user.session_token = response['token'] @@ -83,16 +78,146 @@ class LeapTest # attempts to destroy a user account via the API. # def assert_delete_user(user) - if user && user.ok && user.id && user.session_token && !user.deleted - url = api_url("/1/users/#{user.id}.json") - options = {:headers => { - "Authorization" => "Token token=\"#{user.session_token}\"" - }} - params = { - :identities => 'destroy' + if user.is_a? String + assert_delete_user_by_login(user) + elsif user.is_a? SRP::User + assert_delete_srp_user(user) + end + end + + # + # returns true if the identity exists, uses monitor token auth + # + def identity_exists?(address) + url = api_url("/identities/#{URI.encode(address)}.json") + get(url, nil, api_options(:auth => :monitor)) do |body, response| + return response.code == "200" + end + end + + def upload_public_key(user_id, public_key) + url = api_url("/users/#{user_id}.json") + params = {"user[public_key]" => public_key} + assert_put(url, params, api_options(:auth => :monitor)) + end + + # + # return user document as a Hash. uses monitor token auth + # + def find_user_by_id(user_id) + url = api_url("/users/#{user_id}.json") + assert_get(url, nil, api_options(:auth => :monitor)) do |body| + return JSON.parse(body) + end + end + + # + # return user document as a Hash. uses monitor token auth + # NOTE: this relies on deprecated behavior of the API + # and will not work when multi-domain support is added. + # + def find_user_by_login(login) + url = api_url("/users/0.json?login=#{login}") + options = {:ok_codes => [200, 404]}.merge(api_options(:auth => :monitor)) + assert_get(url, nil, options) do |body, response| + if response.code == "200" + return JSON.parse(body) + else + return nil + end + end + end + + private + + def api_url(path) + unless path =~ /^\// + path = '/' + path + end + if property('testing.api_uri') + return property('testing.api_uri') + path + elsif property('api') + api = property('api') + return "https://%{domain}:%{port}/%{version}#{path}" % { + :domain => api['domain'], + :port => api['port'], + :version => api['version'] || 1 } + else + fail 'This node needs to have either testing.api_url or api.{domain,port} configured.' + end + end + + # + # produces an options hash used for api http requests. + # + # argument options hash gets added to "headers" + # of the http request. + # + # special :auth key in argument will expand to + # add api_token_auth header. + # + # if you want to try manually: + # + # export API_URI=`grep api_uri /etc/leap/hiera.yaml | cut -d\" -f2` + # export TOKEN=`grep monitor_auth_token /etc/leap/hiera.yaml | awk '{print $2}'` + # curl -H "Accept: application/json" -H "Token: $TOKEN" $API_URI + # + def api_options(options={}) + # note: must be :headers, not "headers" + hsh = { + :headers => { + "Accept" => "application/json" + } + } + if options[:auth] + hsh[:headers].merge!(api_token_auth(options.delete(:auth))) + end + hsh[:headers].merge!(options) + return hsh + end + + # + # add token authentication to a http request. + # + # returns a hash suitable for adding to the 'headers' option + # of an http function. + # + def api_token_auth(token) + if token.is_a?(Symbol) && property('testing') + if token == :monitor + token_str = property('testing.monitor_auth_token') + else + raise ArgumentError.new 'no such token' + end + else + token_str = token + end + {"Authorization" => "Token token=\"#{token_str}\""} + end + + # + # not actually used in any test, but useful when + # writing new tests. + # + def assert_delete_user_by_login(login_name) + user = find_user_by_login(login_name) + url = api_url("/users/#{user['id']}.json") + params = {:identities => 'destroy'} + delete(url, params, api_options(:auth => :monitor)) do |body, response, error| + assert error.nil?, "Error deleting user: #{error}" + assert response.code.to_i == 200, "Unable to delete user: HTTP response from API should have code 200, was #{response.code} #{error} #{body}" + assert(response = JSON.parse(body), 'Delete response should be JSON') + assert(response["success"], 'Deleting user should be a success') + end + end + + def assert_delete_srp_user(user) + if user && user.ok && user.id && user.session_token && !user.deleted + url = api_url("users/#{user.id}.json") + params = {:identities => 'destroy'} user.deleted = true - delete(url, params, options) do |body, response, error| + delete(url, params, api_options(:auth => user.session_token)) do |body, response, error| assert error.nil?, "Error deleting user: #{error}" assert response.code.to_i == 200, "Unable to delete user: HTTP response from API should have code 200, was #{response.code} #{error} #{body}" assert(response = JSON.parse(body), 'Delete response should be JSON') @@ -101,4 +226,5 @@ class LeapTest end end + end diff --git a/tests/helpers/couchdb_helper.rb b/tests/helpers/couchdb_helper.rb index 27dabfcb..b9085c1e 100644 --- a/tests/helpers/couchdb_helper.rb +++ b/tests/helpers/couchdb_helper.rb @@ -103,4 +103,40 @@ class LeapTest end end + def assert_destroy_user_db(user_id, options=nil) + db_name = "user-#{user_id}" + url = couchdb_url("/#{db_name}", options) + http_options = {:ok_codes => [200, 404]} # ignore missing dbs + assert_delete(url, nil, http_options) + end + + def assert_create_user_db(user_id, options=nil) + db_name = "user-#{user_id}" + url = couchdb_url("/#{db_name}", options) + http_options = {:ok_codes => [200, 404]} # ignore missing dbs + assert_put(url, nil, :format => :json) do |body| + assert response = JSON.parse(body), "PUT response should be JSON" + assert response["ok"], "PUT response should be OK" + end + end + + # + # returns true if the per-user db created by soledad-server exists. + # + def user_db_exists?(user_id, options=nil) + db_name = "user-#{user_id}" + url = couchdb_url("/#{db_name}", options) + get(url) do |body, response, error| + if response.nil? + fail "could not query couchdb #{url}: #{error}\n#{body}" + elsif response.code.to_i == 200 + return true + elsif response.code.to_i == 404 + return false + else + fail ["could not query couchdb #{url}: expected response code 200 or 404, but got #{response.code}.", error, body].compact.join("\n") + end + end + end + end \ No newline at end of file diff --git a/tests/helpers/http_helper.rb b/tests/helpers/http_helper.rb index 2fcdc910..0d0bb7d5 100644 --- a/tests/helpers/http_helper.rb +++ b/tests/helpers/http_helper.rb @@ -81,20 +81,31 @@ class LeapTest # # calls http_send, yielding results if successful or failing with - # descriptive infor otherwise. + # descriptive info otherwise. + # + # options: + # - error_msg: custom error message to display. + # - ok_codes: in addition to 2xx, codes in this array will not produce an error. # def assert_http_send(method, url, params=nil, options=nil, &block) options ||= {} error_msg = options[:error_msg] || (url.respond_to?(:memo) ? url.memo : nil) http_send(method, url, params, options) do |body, response, error| - ok = response && response.code.to_i >= 200 && response.code.to_i < 300 - if body && ok - if block - yield(body) if block.arity == 1 - yield(response, body) if block.arity == 2 + if response + code = response.code.to_i + ok = code >= 200 && code < 300 + if options[:ok_codes] + ok ||= options[:ok_codes].include?(code) + end + if ok + if block + yield(body) if block.arity == 1 + yield(body, response) if block.arity == 2 + yield(body, response, error) if block.arity == 3 + end + else + fail ["Expected success code from #{method} #{url}, but got #{response.code} instead.", error_msg, body].compact.join("\n") end - elsif response && !ok - fail ["Expected a 200 status code from #{method} #{url}, but got #{response.code} instead.", error_msg, body].compact.join("\n") else fail ["Expected a response from #{method} #{url}, but got \"#{error}\" instead.", error_msg, body].compact.join("\n"), error end diff --git a/tests/helpers/smtp_helper.rb b/tests/helpers/smtp_helper.rb new file mode 100644 index 00000000..ea7fb9fa --- /dev/null +++ b/tests/helpers/smtp_helper.rb @@ -0,0 +1,45 @@ +require 'net/smtp' + +class LeapTest + + TEST_EMAIL_USER = "test_user_email" + TEST_BAD_USER = "test_user_bad" + + MSG_BODY = %(Since it seems that any heart which beats for freedom has the right only to a +lump of lead, I too claim my share. If you let me live, I shall never stop +crying for revenge and I shall avenge my brothers. I have finished. If you are +not cowards, kill me! + +--Louise Michel) + + def send_email(recipient, options={}) + sender = options[:sender] || recipient + helo_domain = property('domain.full_suffix') + headers = { + "Date" => Time.now.utc, + "From" => sender, + "To" => recipient, + "Subject" => "Test Message", + "X-LEAP-TEST" => "true" + }.merge(options[:headers]||{}) + message = [] + headers.each do |key, value| + message << "#{key}: #{value}" + end + message << "" + message << MSG_BODY + Net::SMTP.start('localhost', 25, helo_domain) do |smtp| + smtp.send_message message.join("\n"), recipient, sender + end + end + + def assert_send_email(recipient, options={}) + begin + send_email(recipient, options) + rescue IOError, Net::OpenTimeout, + Net::ReadTimeout, Net::SMTPError => e + fail "Could not send mail to #{recipient} (#{e})" + end + end + +end \ No newline at end of file diff --git a/tests/helpers/srp_helper.rb b/tests/helpers/srp_helper.rb index 5d30b459..b30fa768 100644 --- a/tests/helpers/srp_helper.rb +++ b/tests/helpers/srp_helper.rb @@ -138,8 +138,8 @@ d15dc7d7b46154d6b6ce8ef4ad69b15d4982559b297bcf1885c529f566660e5 attr_accessor :username, :password, :salt, :verifier, :id, :session_token, :ok, :deleted - def initialize - @username = "test_user_" + SecureRandom.urlsafe_base64(10).downcase.gsub(/[_-]/, '') + def initialize(username=nil) + @username = username || "tmp_user_" + SecureRandom.urlsafe_base64(10).downcase.gsub(/[_-]/, '') @password = "password_" + SecureRandom.urlsafe_base64(10) @salt = bigrand(4).hex @verifier = modpow(GENERATOR, private_key) -- cgit v1.2.3 From 91dd5a8b3c6d7bc6b06852c1659564c5559d743d Mon Sep 17 00:00:00 2001 From: elijah Date: Mon, 25 Apr 2016 22:24:05 -0300 Subject: [tests] better error message when identity test cannot contact api. closes #8046 --- tests/helpers/bonafide_helper.rb | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'tests/helpers') diff --git a/tests/helpers/bonafide_helper.rb b/tests/helpers/bonafide_helper.rb index 95e3e3b7..5b886228 100644 --- a/tests/helpers/bonafide_helper.rb +++ b/tests/helpers/bonafide_helper.rb @@ -90,7 +90,10 @@ class LeapTest # def identity_exists?(address) url = api_url("/identities/#{URI.encode(address)}.json") - get(url, nil, api_options(:auth => :monitor)) do |body, response| + options = {:ok_codes => [200, 404]}.merge( + api_options(:auth => :monitor) + ) + assert_get(url, nil, options) do |body, response| return response.code == "200" end end @@ -118,7 +121,9 @@ class LeapTest # def find_user_by_login(login) url = api_url("/users/0.json?login=#{login}") - options = {:ok_codes => [200, 404]}.merge(api_options(:auth => :monitor)) + options = {:ok_codes => [200, 404]}.merge( + api_options(:auth => :monitor) + ) assert_get(url, nil, options) do |body, response| if response.code == "200" return JSON.parse(body) -- cgit v1.2.3