* Package: www-apps/roundup-2.0.0 * Repository: gentoo * Maintainer: web-apps@gentoo.org cedk@gentoo.org * USE: abi_x86_64 amd64 elibc_glibc kernel_linux python_targets_python3_8 sqlite ssl test tz userland_GNU * FEATURES: network-sandbox preserve-libs sandbox splitdebug test userpriv usersandbox >>> Unpacking source... >>> Unpacking roundup-2.0.0.tar.gz to /var/tmp/portage/www-apps/roundup-2.0.0/work >>> Source unpacked in /var/tmp/portage/www-apps/roundup-2.0.0/work >>> Preparing source in /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0 ... >>> Source prepared. >>> Configuring source in /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0 ... >>> Source configured. >>> Compiling source in /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0 ... * python3_8: running distutils-r1_run_phase distutils-r1_python_compile python3.8 setup.py build -j 3 running build *** SOURCE WARNING: The MANIFEST file is missing! creating build creating build/share creating build/share/locale creating build/share/locale/es creating build/share/locale/es/LC_MESSAGES creating build/share/locale/lt creating build/share/locale/lt/LC_MESSAGES creating build/share/locale/ru creating build/share/locale/ru/LC_MESSAGES creating build/share/locale/fr creating build/share/locale/fr/LC_MESSAGES creating build/share/locale/zh_CN creating build/share/locale/zh_CN/LC_MESSAGES creating build/share/locale/zh_TW creating build/share/locale/zh_TW/LC_MESSAGES creating build/share/locale/nb creating build/share/locale/nb/LC_MESSAGES creating build/share/locale/hu creating build/share/locale/hu/LC_MESSAGES creating build/share/locale/ja creating build/share/locale/ja/LC_MESSAGES creating build/share/locale/it creating build/share/locale/it/LC_MESSAGES creating build/share/locale/en creating build/share/locale/en/LC_MESSAGES creating build/share/locale/de creating build/share/locale/de/LC_MESSAGES running build_py creating /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup copying roundup/i18n.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup copying roundup/demo.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup copying roundup/install_util.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup copying roundup/mailer.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup copying roundup/rate_limit.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup copying roundup/admin.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup copying roundup/password.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup copying roundup/instance.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup copying roundup/xmlrpc.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup copying roundup/hyperdb.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup copying roundup/version_check.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup copying roundup/dehtml.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup copying roundup/support.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup copying roundup/roundupdb.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup copying roundup/msgfmt.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup copying roundup/date.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup copying roundup/configuration.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup copying roundup/token.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup copying roundup/mailgw.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup copying roundup/security.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup copying roundup/__init__.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup copying roundup/init.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup copying roundup/rest.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup copying roundup/exceptions.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup copying roundup/actions.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup creating /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/anypy copying roundup/anypy/my_input.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/anypy copying roundup/anypy/http_.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/anypy copying roundup/anypy/strings.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/anypy copying roundup/anypy/cookie_.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/anypy copying roundup/anypy/dbm_.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/anypy copying roundup/anypy/urllib_.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/anypy copying roundup/anypy/random_.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/anypy copying roundup/anypy/xmlrpc_.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/anypy copying roundup/anypy/html.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/anypy copying roundup/anypy/__init__.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/anypy copying roundup/anypy/email_.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/anypy copying roundup/anypy/findargspec.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/anypy copying roundup/anypy/cmp_.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/anypy creating /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/cgi copying roundup/cgi/engine_jinja2.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/cgi copying roundup/cgi/templating.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/cgi copying roundup/cgi/accept_language.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/cgi copying roundup/cgi/engine_zopetal.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/cgi copying roundup/cgi/cgitb.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/cgi copying roundup/cgi/TranslationService.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/cgi copying roundup/cgi/apache.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/cgi copying roundup/cgi/timestamp.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/cgi copying roundup/cgi/wsgi_handler.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/cgi copying roundup/cgi/engine_chameleon.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/cgi copying roundup/cgi/KeywordsExpr.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/cgi copying roundup/cgi/__init__.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/cgi copying roundup/cgi/form_parser.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/cgi copying roundup/cgi/exceptions.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/cgi copying roundup/cgi/client.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/cgi copying roundup/cgi/actions.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/cgi creating /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/cgi/PageTemplates copying roundup/cgi/PageTemplates/TALES.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/cgi/PageTemplates copying roundup/cgi/PageTemplates/GlobalTranslationService.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/cgi/PageTemplates copying roundup/cgi/PageTemplates/PathIterator.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/cgi/PageTemplates copying roundup/cgi/PageTemplates/MultiMapping.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/cgi/PageTemplates copying roundup/cgi/PageTemplates/PageTemplate.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/cgi/PageTemplates copying roundup/cgi/PageTemplates/PythonExpr.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/cgi/PageTemplates copying roundup/cgi/PageTemplates/__init__.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/cgi/PageTemplates copying roundup/cgi/PageTemplates/Expressions.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/cgi/PageTemplates creating /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/cgi/TAL copying roundup/cgi/TAL/DummyEngine.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/cgi/TAL copying roundup/cgi/TAL/markupbase.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/cgi/TAL copying roundup/cgi/TAL/TranslationContext.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/cgi/TAL copying roundup/cgi/TAL/TALInterpreter.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/cgi/TAL copying roundup/cgi/TAL/talgettext.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/cgi/TAL copying roundup/cgi/TAL/HTMLTALParser.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/cgi/TAL copying roundup/cgi/TAL/TALGenerator.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/cgi/TAL copying roundup/cgi/TAL/XMLParser.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/cgi/TAL copying roundup/cgi/TAL/__init__.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/cgi/TAL copying roundup/cgi/TAL/TALParser.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/cgi/TAL copying roundup/cgi/TAL/HTMLParser.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/cgi/TAL copying roundup/cgi/TAL/TALDefs.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/cgi/TAL creating /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/cgi/ZTUtils copying roundup/cgi/ZTUtils/Iterator.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/cgi/ZTUtils copying roundup/cgi/ZTUtils/Batch.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/cgi/ZTUtils copying roundup/cgi/ZTUtils/__init__.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/cgi/ZTUtils creating /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/backends copying roundup/backends/indexer_common.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/backends copying roundup/backends/indexer_dbm.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/backends copying roundup/backends/locking.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/backends copying roundup/backends/indexer_rdbms.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/backends copying roundup/backends/back_anydbm.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/backends copying roundup/backends/blobfiles.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/backends copying roundup/backends/back_postgresql.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/backends copying roundup/backends/indexer_xapian.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/backends copying roundup/backends/sessions_rdbms.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/backends copying roundup/backends/back_mysql.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/backends copying roundup/backends/__init__.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/backends copying roundup/backends/rdbms_common.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/backends copying roundup/backends/indexer_whoosh.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/backends copying roundup/backends/back_sqlite.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/backends copying roundup/backends/portalocker.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/backends copying roundup/backends/sessions_dbm.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/backends creating /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/scripts copying roundup/scripts/roundup_mailgw.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/scripts copying roundup/scripts/roundup_demo.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/scripts copying roundup/scripts/roundup_admin.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/scripts copying roundup/scripts/__init__.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/scripts copying roundup/scripts/roundup_server.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/scripts copying roundup/scripts/roundup_xmlrpc_server.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/scripts copying roundup/scripts/roundup_gettext.py -> /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/lib/roundup/scripts warning: build_py: byte-compiling is disabled, skipping. running build_scripts creating /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/scripts writing /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/scripts/roundup-mailgw writing /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/scripts/roundup-demo writing /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/scripts/roundup-admin writing /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/scripts/roundup-server writing /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/scripts/roundup-xmlrpc-server writing /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0-python3_8/scripts/roundup-gettext >>> Source compiled. >>> Test phase: www-apps/roundup-2.0.0 * python3_8: running distutils-r1_run_phase python_test python3.8 -m pytest -vv -ra -l ============================= test session starts ============================== platform linux -- Python 3.8.10, pytest-6.2.4, py-1.10.0, pluggy-0.13.1 -- /usr/bin/python3.8 cachedir: .pytest_cache hypothesis profile 'default' -> database=DirectoryBasedExampleDatabase('/var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0/.hypothesis/examples') rootdir: /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0 plugins: subtests-0.5.0, mock-3.6.1, ament-lint-0.10.4, ament-flake8-0.10.4, ament-pep257-0.10.4, ament-copyright-0.10.4, rerunfailures-10.0, hypothesis-6.13.10, describe-1.0.0, pkgcore-0.12.1, helpers-namespace-2019.1.8, flake8-1.0.7, asyncio-0.15.1, timeout-1.4.2 collecting ... collected 1473 items test/test_actions.py::ShowActionTestCase::testShowAction PASSED [ 0%] test/test_actions.py::ShowActionTestCase::testShowActionNoType PASSED [ 0%] test/test_actions.py::RetireActionTestCase::testDontRetireAdminOrAnonymous PASSED [ 0%] test/test_actions.py::RetireActionTestCase::testNoPermission PASSED [ 0%] test/test_actions.py::RetireActionTestCase::testRetireAction PASSED [ 0%] test/test_actions.py::RestoreActionTestCase::testNoPermission PASSED [ 0%] test/test_actions.py::RestoreActionTestCase::testRestoreAction PASSED [ 0%] test/test_actions.py::StandardSearchActionTestCase::testNoPermission PASSED [ 0%] test/test_actions.py::StandardSearchActionTestCase::testQueryName PASSED [ 0%] test/test_actions.py::FakeFilterVarsTestCase::testEmptyKey PASSED [ 0%] test/test_actions.py::FakeFilterVarsTestCase::testEmptyMultilink PASSED [ 0%] test/test_actions.py::FakeFilterVarsTestCase::testIntKey PASSED [ 0%] test/test_actions.py::FakeFilterVarsTestCase::testNonEmptyMultilink PASSED [ 0%] test/test_actions.py::FakeFilterVarsTestCase::testNumKey PASSED [ 0%] test/test_actions.py::FakeFilterVarsTestCase::testStandardKey PASSED [ 1%] test/test_actions.py::FakeFilterVarsTestCase::testStringKey PASSED [ 1%] test/test_actions.py::FakeFilterVarsTestCase::testTokenizedStringKey PASSED [ 1%] test/test_actions.py::CollisionDetectionTestCase::testCollision PASSED [ 1%] test/test_actions.py::CollisionDetectionTestCase::testLastNodeActivity PASSED [ 1%] test/test_actions.py::CollisionDetectionTestCase::testLastUserActivity PASSED [ 1%] test/test_actions.py::LoginTestCase::testCorrectLogin PASSED [ 1%] test/test_actions.py::LoginTestCase::testCorrectLoginRedirect PASSED [ 1%] test/test_actions.py::LoginTestCase::testInvalidLoginRedirect PASSED [ 1%] test/test_actions.py::LoginTestCase::testInvalidPassword PASSED [ 1%] test/test_actions.py::LoginTestCase::testInvalidUsername PASSED [ 1%] test/test_actions.py::LoginTestCase::testLoginRateLimit PASSED [ 1%] test/test_actions.py::LoginTestCase::testLoginRateLimitOff PASSED [ 1%] test/test_actions.py::LoginTestCase::testNoUsername PASSED [ 1%] test/test_actions.py::LoginTestCase::testNoWebAccess PASSED [ 1%] test/test_actions.py::EditItemActionTestCase::testFileAttach PASSED [ 2%] test/test_actions.py::EditItemActionTestCase::testLinkExisting PASSED [ 2%] test/test_actions.py::EditItemActionTestCase::testLinkNewToExisting PASSED [ 2%] test/test_actions.py::EditItemActionTestCase::testMessageAttach PASSED [ 2%] test/test_actions.py::EditItemActionTestCase::testMessageMultiAttach PASSED [ 2%] test/test_admin.py::anydbmAdminTest::testCliParse PASSED [ 2%] test/test_admin.py::anydbmAdminTest::testFilter PASSED [ 2%] test/test_admin.py::anydbmAdminTest::testFind PASSED [ 2%] test/test_admin.py::anydbmAdminTest::testGenconfigUpdate PASSED [ 2%] test/test_admin.py::anydbmAdminTest::testGet PASSED [ 2%] test/test_admin.py::anydbmAdminTest::testInit PASSED [ 2%] test/test_admin.py::anydbmAdminTest::testInitWithConfig_ini PASSED [ 2%] test/test_admin.py::anydbmAdminTest::testSet PASSED [ 2%] test/test_admin.py::anydbmAdminTest::testSetOnClass PASSED [ 2%] test/test_admin.py::anydbmAdminTest::testSpecification PASSED [ 2%] test/test_admin.py::anydbmAdminTest::testTable PASSED [ 3%] test/test_admin.py::mysqlAdminTest::testCliParse SKIPPED (Skipping M...) [ 3%] test/test_admin.py::mysqlAdminTest::testFilter SKIPPED (Skipping MyS...) [ 3%] test/test_admin.py::mysqlAdminTest::testFind SKIPPED (Skipping MySQL...) [ 3%] test/test_admin.py::mysqlAdminTest::testGenconfigUpdate SKIPPED (Ski...) [ 3%] test/test_admin.py::mysqlAdminTest::testGet SKIPPED (Skipping MySQL ...) [ 3%] test/test_admin.py::mysqlAdminTest::testInit SKIPPED (Skipping MySQL...) [ 3%] test/test_admin.py::mysqlAdminTest::testInitWithConfig_ini SKIPPED (...) [ 3%] test/test_admin.py::mysqlAdminTest::testSet SKIPPED (Skipping MySQL ...) [ 3%] test/test_admin.py::mysqlAdminTest::testSetOnClass SKIPPED (Skipping...) [ 3%] test/test_admin.py::mysqlAdminTest::testSpecification SKIPPED (Skipp...) [ 3%] test/test_admin.py::mysqlAdminTest::testTable SKIPPED (Skipping MySQ...) [ 3%] test/test_admin.py::sqliteAdminTest::testCliParse PASSED [ 3%] test/test_admin.py::sqliteAdminTest::testFilter PASSED [ 3%] test/test_admin.py::sqliteAdminTest::testFind PASSED [ 4%] test/test_admin.py::sqliteAdminTest::testGenconfigUpdate PASSED [ 4%] test/test_admin.py::sqliteAdminTest::testGet PASSED [ 4%] test/test_admin.py::sqliteAdminTest::testInit PASSED [ 4%] test/test_admin.py::sqliteAdminTest::testInitWithConfig_ini PASSED [ 4%] test/test_admin.py::sqliteAdminTest::testSet PASSED [ 4%] test/test_admin.py::sqliteAdminTest::testSetOnClass PASSED [ 4%] test/test_admin.py::sqliteAdminTest::testSpecification PASSED [ 4%] test/test_admin.py::sqliteAdminTest::testTable PASSED [ 4%] test/test_admin.py::postgresqlAdminTest::testCliParse SKIPPED (Skipp...) [ 4%] test/test_admin.py::postgresqlAdminTest::testFilter SKIPPED (Skippin...) [ 4%] test/test_admin.py::postgresqlAdminTest::testFind SKIPPED (Skipping ...) [ 4%] test/test_admin.py::postgresqlAdminTest::testGenconfigUpdate SKIPPED [ 4%] test/test_admin.py::postgresqlAdminTest::testGet SKIPPED (Skipping P...) [ 4%] test/test_admin.py::postgresqlAdminTest::testInit SKIPPED (Skipping ...) [ 4%] test/test_admin.py::postgresqlAdminTest::testInitWithConfig_ini SKIPPED [ 5%] test/test_admin.py::postgresqlAdminTest::testSet SKIPPED (Skipping P...) [ 5%] test/test_admin.py::postgresqlAdminTest::testSetOnClass SKIPPED (Ski...) [ 5%] test/test_admin.py::postgresqlAdminTest::testSpecification SKIPPED (...) [ 5%] test/test_admin.py::postgresqlAdminTest::testTable SKIPPED (Skipping...) [ 5%] test/test_anydbm.py::anydbmDBTest::testActorProperty <- test/db_test_base.py PASSED [ 5%] test/test_anydbm.py::anydbmDBTest::testAddProperty <- test/db_test_base.py PASSED [ 5%] test/test_anydbm.py::anydbmDBTest::testAddRemoveProperty <- test/db_test_base.py PASSED [ 5%] test/test_anydbm.py::anydbmDBTest::testAdminDuplicateInitialisation <- test/db_test_base.py PASSED [ 5%] test/test_anydbm.py::anydbmDBTest::testAdminImportExport <- test/db_test_base.py PASSED [ 5%] test/test_anydbm.py::anydbmDBTest::testAdminOtherCommands <- test/db_test_base.py PASSED [ 5%] test/test_anydbm.py::anydbmDBTest::testAuditorTwo <- test/db_test_base.py PASSED [ 5%] test/test_anydbm.py::anydbmDBTest::testAuditors <- test/db_test_base.py PASSED [ 5%] test/test_anydbm.py::anydbmDBTest::testBooleanChange <- test/db_test_base.py PASSED [ 5%] test/test_anydbm.py::anydbmDBTest::testBooleanSet <- test/db_test_base.py PASSED [ 5%] test/test_anydbm.py::anydbmDBTest::testBooleanUnset <- test/db_test_base.py PASSED [ 6%] test/test_anydbm.py::anydbmDBTest::testCacheCreateSet <- test/db_test_base.py PASSED [ 6%] test/test_anydbm.py::anydbmDBTest::testCreatorProperty <- test/db_test_base.py PASSED [ 6%] test/test_anydbm.py::anydbmDBTest::testDateChange <- test/db_test_base.py PASSED [ 6%] test/test_anydbm.py::anydbmDBTest::testDateLeapYear <- test/db_test_base.py PASSED [ 6%] test/test_anydbm.py::anydbmDBTest::testDateSort <- test/db_test_base.py PASSED [ 6%] test/test_anydbm.py::anydbmDBTest::testDateSortMultilink <- test/db_test_base.py PASSED [ 6%] test/test_anydbm.py::anydbmDBTest::testDateUnset <- test/db_test_base.py PASSED [ 6%] test/test_anydbm.py::anydbmDBTest::testDefault_Value <- test/db_test_base.py PASSED [ 6%] test/test_anydbm.py::anydbmDBTest::testDestroyBlob <- test/db_test_base.py PASSED [ 6%] test/test_anydbm.py::anydbmDBTest::testDestroyJournalling <- test/db_test_base.py PASSED [ 6%] test/test_anydbm.py::anydbmDBTest::testDestroyNoJournalling <- test/db_test_base.py PASSED [ 6%] test/test_anydbm.py::anydbmDBTest::testDoubleChange <- test/db_test_base.py PASSED [ 6%] test/test_anydbm.py::anydbmDBTest::testDoubleUnset <- test/db_test_base.py PASSED [ 6%] test/test_anydbm.py::anydbmDBTest::testEmptySet <- test/db_test_base.py PASSED [ 6%] test/test_anydbm.py::anydbmDBTest::testExceptions <- test/db_test_base.py PASSED [ 7%] test/test_anydbm.py::anydbmDBTest::testFileClassContentChange <- test/db_test_base.py PASSED [ 7%] test/test_anydbm.py::anydbmDBTest::testFileClassIndexingNoNoNo <- test/db_test_base.py PASSED [ 7%] test/test_anydbm.py::anydbmDBTest::testFileClassReindexing <- test/db_test_base.py PASSED [ 7%] test/test_anydbm.py::anydbmDBTest::testFilteringBoolean <- test/db_test_base.py PASSED [ 7%] test/test_anydbm.py::anydbmDBTest::testFilteringDateRangeMulti <- test/db_test_base.py PASSED [ 7%] test/test_anydbm.py::anydbmDBTest::testFilteringDateSort <- test/db_test_base.py PASSED [ 7%] test/test_anydbm.py::anydbmDBTest::testFilteringDateSortPriorityGroup <- test/db_test_base.py PASSED [ 7%] test/test_anydbm.py::anydbmDBTest::testFilteringID <- test/db_test_base.py PASSED [ 7%] test/test_anydbm.py::anydbmDBTest::testFilteringIntervalSort <- test/db_test_base.py PASSED [ 7%] test/test_anydbm.py::anydbmDBTest::testFilteringLink <- test/db_test_base.py PASSED [ 7%] test/test_anydbm.py::anydbmDBTest::testFilteringLinkSortGroup <- test/db_test_base.py PASSED [ 7%] test/test_anydbm.py::anydbmDBTest::testFilteringLinkSortSearchMultilink <- test/db_test_base.py PASSED [ 7%] test/test_anydbm.py::anydbmDBTest::testFilteringMany <- test/db_test_base.py PASSED [ 7%] test/test_anydbm.py::anydbmDBTest::testFilteringMultilink <- test/db_test_base.py PASSED [ 8%] test/test_anydbm.py::anydbmDBTest::testFilteringMultilinkAndGroup <- test/db_test_base.py PASSED [ 8%] test/test_anydbm.py::anydbmDBTest::testFilteringMultilinkSort <- test/db_test_base.py PASSED [ 8%] test/test_anydbm.py::anydbmDBTest::testFilteringMultilinkSortGroup <- test/db_test_base.py PASSED [ 8%] test/test_anydbm.py::anydbmDBTest::testFilteringNumber <- test/db_test_base.py PASSED [ 8%] test/test_anydbm.py::anydbmDBTest::testFilteringRangeBasic <- test/db_test_base.py PASSED [ 8%] test/test_anydbm.py::anydbmDBTest::testFilteringRangeGeekInterval <- test/db_test_base.py PASSED [ 8%] test/test_anydbm.py::anydbmDBTest::testFilteringRangeInterval <- test/db_test_base.py PASSED [ 8%] test/test_anydbm.py::anydbmDBTest::testFilteringRangeMonths <- test/db_test_base.py PASSED [ 8%] test/test_anydbm.py::anydbmDBTest::testFilteringRangeTwoSyntaxes <- test/db_test_base.py PASSED [ 8%] test/test_anydbm.py::anydbmDBTest::testFilteringRangeYearMonthDay <- test/db_test_base.py PASSED [ 8%] test/test_anydbm.py::anydbmDBTest::testFilteringRetired <- test/db_test_base.py PASSED [ 8%] test/test_anydbm.py::anydbmDBTest::testFilteringRetiredString <- test/db_test_base.py PASSED [ 8%] test/test_anydbm.py::anydbmDBTest::testFilteringRevLink <- test/db_test_base.py PASSED [ 8%] test/test_anydbm.py::anydbmDBTest::testFilteringRevMultilink <- test/db_test_base.py PASSED [ 8%] test/test_anydbm.py::anydbmDBTest::testFilteringSortId <- test/db_test_base.py PASSED [ 9%] test/test_anydbm.py::anydbmDBTest::testFilteringSpecialChars <- test/db_test_base.py PASSED [ 9%] test/test_anydbm.py::anydbmDBTest::testFilteringString <- test/db_test_base.py PASSED [ 9%] test/test_anydbm.py::anydbmDBTest::testFilteringStringCase <- test/db_test_base.py PASSED [ 9%] test/test_anydbm.py::anydbmDBTest::testFilteringStringExactMatch <- test/db_test_base.py PASSED [ 9%] test/test_anydbm.py::anydbmDBTest::testFilteringStringSort <- test/db_test_base.py PASSED [ 9%] test/test_anydbm.py::anydbmDBTest::testFilteringTransitiveLinkIssue <- test/db_test_base.py PASSED [ 9%] test/test_anydbm.py::anydbmDBTest::testFilteringTransitiveLinkSort <- test/db_test_base.py PASSED [ 9%] test/test_anydbm.py::anydbmDBTest::testFilteringTransitiveLinkSortNull <- test/db_test_base.py PASSED [ 9%] test/test_anydbm.py::anydbmDBTest::testFilteringTransitiveLinkUser <- test/db_test_base.py PASSED [ 9%] test/test_anydbm.py::anydbmDBTest::testFilteringTransitiveLinkUserLimit <- test/db_test_base.py PASSED [ 9%] test/test_anydbm.py::anydbmDBTest::testFilteringTransitiveMultilink <- test/db_test_base.py PASSED [ 9%] test/test_anydbm.py::anydbmDBTest::testFilteringTransitiveMultilinkSort <- test/db_test_base.py PASSED [ 9%] test/test_anydbm.py::anydbmDBTest::testFindIncorrectProperty <- test/db_test_base.py PASSED [ 9%] test/test_anydbm.py::anydbmDBTest::testFindLink <- test/db_test_base.py PASSED [ 9%] test/test_anydbm.py::anydbmDBTest::testFindLinkAndMultilink <- test/db_test_base.py PASSED [ 10%] test/test_anydbm.py::anydbmDBTest::testFindLinkFail <- test/db_test_base.py PASSED [ 10%] test/test_anydbm.py::anydbmDBTest::testFindLinkUnset <- test/db_test_base.py PASSED [ 10%] test/test_anydbm.py::anydbmDBTest::testFindMultiMultilink <- test/db_test_base.py PASSED [ 10%] test/test_anydbm.py::anydbmDBTest::testFindMultilink <- test/db_test_base.py PASSED [ 10%] test/test_anydbm.py::anydbmDBTest::testFindMultilinkFail <- test/db_test_base.py PASSED [ 10%] test/test_anydbm.py::anydbmDBTest::testFindMultilinkUnset <- test/db_test_base.py PASSED [ 10%] test/test_anydbm.py::anydbmDBTest::testFindMultipleLink <- test/db_test_base.py PASSED [ 10%] test/test_anydbm.py::anydbmDBTest::testFindRetired <- test/db_test_base.py PASSED [ 10%] test/test_anydbm.py::anydbmDBTest::testFindRevLinkMultilink <- test/db_test_base.py PASSED [ 10%] test/test_anydbm.py::anydbmDBTest::testForcedReindexing <- test/db_test_base.py PASSED [ 10%] test/test_anydbm.py::anydbmDBTest::testIDGeneration <- test/db_test_base.py PASSED [ 10%] test/test_anydbm.py::anydbmDBTest::testIDSetting <- test/db_test_base.py PASSED [ 10%] test/test_anydbm.py::anydbmDBTest::testImportExport <- test/db_test_base.py PASSED [ 10%] test/test_anydbm.py::anydbmDBTest::testIndexerSearchMulti <- test/db_test_base.py PASSED [ 10%] test/test_anydbm.py::anydbmDBTest::testIndexerSearching <- test/db_test_base.py PASSED [ 11%] test/test_anydbm.py::anydbmDBTest::testIndexerSearchingLink <- test/db_test_base.py PASSED [ 11%] test/test_anydbm.py::anydbmDBTest::testIndexingPropertiesOnImport <- test/db_test_base.py PASSED [ 11%] test/test_anydbm.py::anydbmDBTest::testIntegerChange <- test/db_test_base.py PASSED [ 11%] test/test_anydbm.py::anydbmDBTest::testIntegerUnset <- test/db_test_base.py PASSED [ 11%] test/test_anydbm.py::anydbmDBTest::testIntervalChange <- test/db_test_base.py PASSED [ 11%] test/test_anydbm.py::anydbmDBTest::testIntervalUnset <- test/db_test_base.py PASSED [ 11%] test/test_anydbm.py::anydbmDBTest::testJournalNonexistingProperty <- test/db_test_base.py PASSED [ 11%] test/test_anydbm.py::anydbmDBTest::testJournalPreCommit <- test/db_test_base.py PASSED [ 11%] test/test_anydbm.py::anydbmDBTest::testJournals <- test/db_test_base.py PASSED [ 11%] test/test_anydbm.py::anydbmDBTest::testKeyValue <- test/db_test_base.py PASSED [ 11%] test/test_anydbm.py::anydbmDBTest::testLabelProp <- test/db_test_base.py PASSED [ 11%] test/test_anydbm.py::anydbmDBTest::testLinkChange <- test/db_test_base.py PASSED [ 11%] test/test_anydbm.py::anydbmDBTest::testLinkUnset <- test/db_test_base.py PASSED [ 11%] test/test_anydbm.py::anydbmDBTest::testMakeSeveralMultilinkedNodes <- test/db_test_base.py PASSED [ 12%] test/test_anydbm.py::anydbmDBTest::testMultilinkChange <- test/db_test_base.py PASSED [ 12%] test/test_anydbm.py::anydbmDBTest::testMultilinkChangeIterable <- test/db_test_base.py PASSED [ 12%] test/test_anydbm.py::anydbmDBTest::testNosyMail <- test/db_test_base.py PASSED [ 12%] test/test_anydbm.py::anydbmDBTest::testNosyMailTextAndBinary <- test/db_test_base.py PASSED [ 12%] test/test_anydbm.py::anydbmDBTest::testNumberChange <- test/db_test_base.py PASSED [ 12%] test/test_anydbm.py::anydbmDBTest::testNumberUnset <- test/db_test_base.py PASSED [ 12%] test/test_anydbm.py::anydbmDBTest::testPGPNosyMail <- test/db_test_base.py SKIPPED [ 12%] test/test_anydbm.py::anydbmDBTest::testPack <- test/db_test_base.py PASSED [ 12%] test/test_anydbm.py::anydbmDBTest::testPasswordChange <- test/db_test_base.py PASSED [ 12%] test/test_anydbm.py::anydbmDBTest::testPasswordUnset <- test/db_test_base.py PASSED [ 12%] test/test_anydbm.py::anydbmDBTest::testQuietChangenote <- test/db_test_base.py PASSED [ 12%] test/test_anydbm.py::anydbmDBTest::testQuietJournal <- test/db_test_base.py PASSED [ 12%] test/test_anydbm.py::anydbmDBTest::testQuietProperty <- test/db_test_base.py PASSED [ 12%] test/test_anydbm.py::anydbmDBTest::testRefresh <- test/db_test_base.py PASSED [ 12%] test/test_anydbm.py::anydbmDBTest::testReindexingChange <- test/db_test_base.py PASSED [ 13%] test/test_anydbm.py::anydbmDBTest::testReindexingClear <- test/db_test_base.py PASSED [ 13%] test/test_anydbm.py::anydbmDBTest::testRemoveProperty <- test/db_test_base.py PASSED [ 13%] test/test_anydbm.py::anydbmDBTest::testRetire <- test/db_test_base.py PASSED [ 13%] test/test_anydbm.py::anydbmDBTest::testSerialisation <- test/db_test_base.py PASSED [ 13%] test/test_anydbm.py::anydbmDBTest::testStringBinary <- test/db_test_base.py PASSED [ 13%] test/test_anydbm.py::anydbmDBTest::testStringChange <- test/db_test_base.py PASSED [ 13%] test/test_anydbm.py::anydbmDBTest::testStringFind <- test/db_test_base.py PASSED [ 13%] test/test_anydbm.py::anydbmDBTest::testStringUnicode <- test/db_test_base.py PASSED [ 13%] test/test_anydbm.py::anydbmDBTest::testStringUnset <- test/db_test_base.py PASSED [ 13%] test/test_anydbm.py::anydbmDBTest::testTransactions <- test/db_test_base.py PASSED [ 13%] test/test_anydbm.py::anydbmDBTest::testViewPremJournal <- test/db_test_base.py PASSED [ 13%] test/test_anydbm.py::anydbmROTest::testExceptions <- test/db_test_base.py PASSED [ 13%] test/test_anydbm.py::anydbmSchemaTest::test_addNewClass <- test/db_test_base.py PASSED [ 13%] test/test_anydbm.py::anydbmSchemaTest::test_changeClassKey <- test/db_test_base.py PASSED [ 13%] test/test_anydbm.py::anydbmSchemaTest::test_fileClassProps <- test/db_test_base.py PASSED [ 14%] test/test_anydbm.py::anydbmSchemaTest::test_makeNewMultilink <- test/db_test_base.py PASSED [ 14%] test/test_anydbm.py::anydbmSchemaTest::test_modifyClass <- test/db_test_base.py PASSED [ 14%] test/test_anydbm.py::anydbmSchemaTest::test_removeClass <- test/db_test_base.py PASSED [ 14%] test/test_anydbm.py::anydbmSchemaTest::test_removeClassKey <- test/db_test_base.py PASSED [ 14%] test/test_anydbm.py::anydbmSchemaTest::test_removeMultilink <- test/db_test_base.py PASSED [ 14%] test/test_anydbm.py::anydbmSchemaTest::test_reservedProperties <- test/db_test_base.py PASSED [ 14%] test/test_anydbm.py::anydbmClassicInitTest::testCreation <- test/db_test_base.py PASSED [ 14%] test/test_anydbm.py::anydbmHTMLItemTest::testHTMLItemAttributes <- test/db_test_base.py PASSED [ 14%] test/test_anydbm.py::anydbmHTMLItemTest::testHTMLItemDerefFail <- test/db_test_base.py PASSED [ 14%] test/test_anydbm.py::anydbmHTMLItemTest::testHTMLItemDereference <- test/db_test_base.py PASSED [ 14%] test/test_anydbm.py::anydbmSessionTest::testDestroy <- test/session_common.py PASSED [ 14%] test/test_anydbm.py::anydbmSessionTest::testGetAll <- test/session_common.py PASSED [ 14%] test/test_anydbm.py::anydbmSessionTest::testList <- test/session_common.py PASSED [ 14%] test/test_anydbm.py::anydbmSessionTest::testSetSession <- test/session_common.py PASSED [ 15%] test/test_anydbm.py::anydbmSessionTest::testUpdateSession <- test/session_common.py PASSED [ 15%] test/test_anydbm.py::anydbmSpecialActionTestCase::testInnerMain <- test/db_test_base.py PASSED [ 15%] test/test_anydbm.py::anydbmRestTest::testAcceptHeaderParsing <- test/rest_common.py FAILED [ 15%] test/test_anydbm.py::anydbmRestTest::testAuthAllowedPost <- test/rest_common.py FAILED [ 15%] test/test_anydbm.py::anydbmRestTest::testAuthAllowedPut <- test/rest_common.py FAILED [ 15%] test/test_anydbm.py::anydbmRestTest::testAuthDeniedPost <- test/rest_common.py FAILED [ 15%] test/test_anydbm.py::anydbmRestTest::testAuthDeniedPut <- test/rest_common.py FAILED [ 15%] test/test_anydbm.py::anydbmRestTest::testBinaryFieldStorage <- test/rest_common.py FAILED [ 15%] test/test_anydbm.py::anydbmRestTest::testDeleteAttributeUri <- test/rest_common.py FAILED [ 15%] test/test_anydbm.py::anydbmRestTest::testDispatch <- test/rest_common.py FAILED [ 15%] test/test_anydbm.py::anydbmRestTest::testDispatchPost <- test/rest_common.py FAILED [ 15%] test/test_anydbm.py::anydbmRestTest::testEtagGeneration <- test/rest_common.py FAILED [ 15%] test/test_anydbm.py::anydbmRestTest::testEtagProcessing <- test/rest_common.py FAILED [ 15%] test/test_anydbm.py::anydbmRestTest::testFilter <- test/rest_common.py FAILED [ 15%] test/test_anydbm.py::anydbmRestTest::testGet <- test/rest_common.py FAILED [ 16%] test/test_anydbm.py::anydbmRestTest::testGetExactMatch <- test/rest_common.py FAILED [ 16%] test/test_anydbm.py::anydbmRestTest::testGetTransitive <- test/rest_common.py FAILED [ 16%] test/test_anydbm.py::anydbmRestTest::testMethodOverride <- test/rest_common.py FAILED [ 16%] test/test_anydbm.py::anydbmRestTest::testOutputFormat <- test/rest_common.py FAILED [ 16%] test/test_anydbm.py::anydbmRestTest::testPagination <- test/rest_common.py FAILED [ 16%] test/test_anydbm.py::anydbmRestTest::testPatchAction <- test/rest_common.py FAILED [ 16%] test/test_anydbm.py::anydbmRestTest::testPatchAdd <- test/rest_common.py FAILED [ 16%] test/test_anydbm.py::anydbmRestTest::testPatchRemove <- test/rest_common.py FAILED [ 16%] test/test_anydbm.py::anydbmRestTest::testPatchRemoveAll <- test/rest_common.py FAILED [ 16%] test/test_anydbm.py::anydbmRestTest::testPatchReplace <- test/rest_common.py FAILED [ 16%] test/test_anydbm.py::anydbmRestTest::testPost <- test/rest_common.py FAILED [ 16%] test/test_anydbm.py::anydbmRestTest::testPostFile <- test/rest_common.py FAILED [ 16%] test/test_anydbm.py::anydbmRestTest::testPostPOE <- test/rest_common.py FAILED [ 16%] test/test_anydbm.py::anydbmRestTest::testPutAttribute <- test/rest_common.py FAILED [ 16%] test/test_anydbm.py::anydbmRestTest::testPutElement <- test/rest_common.py FAILED [ 17%] test/test_anydbm.py::anydbmRestTest::testRestRateLimit <- test/rest_common.py FAILED [ 17%] test/test_anydbm.py::anydbmRestTest::testSorting <- test/rest_common.py FAILED [ 17%] test/test_anydbm.py::anydbmRestTest::testStatsGen <- test/rest_common.py FAILED [ 17%] test/test_anydbm.py::anydbmRestTest::testTransitiveField <- test/rest_common.py FAILED [ 17%] test/test_anydbm.py::anydbmRestTest::test_bad_audience_jwt <- test/rest_common.py FAILED [ 17%] test/test_anydbm.py::anydbmRestTest::test_bad_issue_jwt <- test/rest_common.py FAILED [ 17%] test/test_anydbm.py::anydbmRestTest::test_bad_roles_jwt <- test/rest_common.py FAILED [ 17%] test/test_anydbm.py::anydbmRestTest::test_bad_subject_jwt <- test/rest_common.py FAILED [ 17%] test/test_anydbm.py::anydbmRestTest::test_disabled_jwt <- test/rest_common.py FAILED [ 17%] test/test_anydbm.py::anydbmRestTest::test_expired_jwt <- test/rest_common.py FAILED [ 17%] test/test_anydbm.py::anydbmRestTest::test_user_email_jwt <- test/rest_common.py FAILED [ 17%] test/test_anydbm.py::anydbmRestTest::test_user_emailnorest_jwt <- test/rest_common.py FAILED [ 17%] test/test_anydbm.py::anydbmRestTest::test_user_jwt <- test/rest_common.py FAILED [ 17%] test/test_cgi.py::MessageTestCase::testAddMessageBAD PASSED [ 17%] test/test_cgi.py::MessageTestCase::testAddMessageNoEscape PASSED [ 18%] test/test_cgi.py::MessageTestCase::testAddMessageOK PASSED [ 18%] test/test_cgi.py::FormTestCase::testAddRemoveNonexistant PASSED [ 18%] test/test_cgi.py::FormTestCase::testBackwardsCompat PASSED [ 18%] test/test_cgi.py::FormTestCase::testCSVExport PASSED [ 18%] test/test_cgi.py::FormTestCase::testCSVExportBadColumnName PASSED [ 18%] test/test_cgi.py::FormTestCase::testCSVExportCharset PASSED [ 18%] test/test_cgi.py::FormTestCase::testCSVExportFailPermissionBadColumn PASSED [ 18%] test/test_cgi.py::FormTestCase::testCSVExportFailPermissionValidColumn PASSED [ 18%] test/test_cgi.py::FormTestCase::testCSVExportWithId PASSED [ 18%] test/test_cgi.py::FormTestCase::testCSVExportWithIdBadColumnName PASSED [ 18%] test/test_cgi.py::FormTestCase::testCSVExportWithIdFailPermissionBadColumn PASSED [ 18%] test/test_cgi.py::FormTestCase::testCSVExportWithIdFailPermissionValidColumn PASSED [ 18%] test/test_cgi.py::FormTestCase::testCheckAndPropertyPermission PASSED [ 18%] test/test_cgi.py::FormTestCase::testClassPermission PASSED [ 19%] test/test_cgi.py::FormTestCase::testCreatePermission PASSED [ 19%] test/test_cgi.py::FormTestCase::testCsrfProtection PASSED [ 19%] test/test_cgi.py::FormTestCase::testEditCSVKeyword PASSED [ 19%] test/test_cgi.py::FormTestCase::testEditCSVRestore PASSED [ 19%] test/test_cgi.py::FormTestCase::testEditCSVTest PASSED [ 19%] test/test_cgi.py::FormTestCase::testEditCSVTestBadRow PASSED [ 19%] test/test_cgi.py::FormTestCase::testEditFileClassAttributes PASSED [ 19%] test/test_cgi.py::FormTestCase::testEditNonexistant PASSED [ 19%] test/test_cgi.py::FormTestCase::testEmptyBoolean PASSED [ 19%] test/test_cgi.py::FormTestCase::testEmptyBooleanSet PASSED [ 19%] test/test_cgi.py::FormTestCase::testEmptyDate PASSED [ 19%] test/test_cgi.py::FormTestCase::testEmptyDateSet PASSED [ 19%] test/test_cgi.py::FormTestCase::testEmptyInteger PASSED [ 19%] test/test_cgi.py::FormTestCase::testEmptyIntegerSet PASSED [ 19%] test/test_cgi.py::FormTestCase::testEmptyLink PASSED [ 20%] test/test_cgi.py::FormTestCase::testEmptyMultilink PASSED [ 20%] test/test_cgi.py::FormTestCase::testEmptyMultilinkSet PASSED [ 20%] test/test_cgi.py::FormTestCase::testEmptyNumber PASSED [ 20%] test/test_cgi.py::FormTestCase::testEmptyNumberSet PASSED [ 20%] test/test_cgi.py::FormTestCase::testEmptyPassword PASSED [ 20%] test/test_cgi.py::FormTestCase::testEmptyPasswordNotSet PASSED [ 20%] test/test_cgi.py::FormTestCase::testEmptyString PASSED [ 20%] test/test_cgi.py::FormTestCase::testEmptyStringSet PASSED [ 20%] test/test_cgi.py::FormTestCase::testFileUpload PASSED [ 20%] test/test_cgi.py::FormTestCase::testFormValuePreserveOnError PASSED [ 20%] test/test_cgi.py::FormTestCase::testHttpProxyStrip PASSED [ 20%] test/test_cgi.py::FormTestCase::testInvalidDate PASSED [ 20%] test/test_cgi.py::FormTestCase::testInvalidInteger PASSED [ 20%] test/test_cgi.py::FormTestCase::testInvalidLinkValue PASSED [ 20%] test/test_cgi.py::FormTestCase::testInvalidMultilinkValue PASSED [ 21%] test/test_cgi.py::FormTestCase::testInvalidNumber PASSED [ 21%] test/test_cgi.py::FormTestCase::testLabelMatching PASSED [ 21%] test/test_cgi.py::FormTestCase::testLinkBadDesignator PASSED [ 21%] test/test_cgi.py::FormTestCase::testLinkNotLink PASSED [ 21%] test/test_cgi.py::FormTestCase::testLinking PASSED [ 21%] test/test_cgi.py::FormTestCase::testMessages PASSED [ 21%] test/test_cgi.py::FormTestCase::testMixedMultilink PASSED [ 21%] test/test_cgi.py::FormTestCase::testMultilinkAdd PASSED [ 21%] test/test_cgi.py::FormTestCase::testMultilinkAddNew PASSED [ 21%] test/test_cgi.py::FormTestCase::testMultilinkRemove PASSED [ 21%] test/test_cgi.py::FormTestCase::testMultilinkRetired PASSED [ 21%] test/test_cgi.py::FormTestCase::testMultiple PASSED [ 21%] test/test_cgi.py::FormTestCase::testMultipleExistingContext PASSED [ 21%] test/test_cgi.py::FormTestCase::testMultipleFileUpload PASSED [ 21%] test/test_cgi.py::FormTestCase::testNothing PASSED [ 22%] test/test_cgi.py::FormTestCase::testNothingWithRequired PASSED [ 22%] test/test_cgi.py::FormTestCase::testPasswordConfigOption PASSED [ 22%] test/test_cgi.py::FormTestCase::testPasswordMigration PASSED [ 22%] test/test_cgi.py::FormTestCase::testRegisterActionDelay PASSED [ 22%] test/test_cgi.py::FormTestCase::testRegisterActionUnusedUserCheck PASSED [ 22%] test/test_cgi.py::FormTestCase::testRequiredBoolean PASSED [ 22%] test/test_cgi.py::FormTestCase::testRequiredInteger PASSED [ 22%] test/test_cgi.py::FormTestCase::testRequiredNumber PASSED [ 22%] test/test_cgi.py::FormTestCase::testRestCsrfProtection PASSED [ 22%] test/test_cgi.py::FormTestCase::testRoles PASSED [ 22%] test/test_cgi.py::FormTestCase::testSearchPermission PASSED [ 22%] test/test_cgi.py::FormTestCase::testSetBoolean PASSED [ 22%] test/test_cgi.py::FormTestCase::testSetDate PASSED [ 22%] test/test_cgi.py::FormTestCase::testSetInteger PASSED [ 23%] test/test_cgi.py::FormTestCase::testSetIntegerReplaceNone PASSED [ 23%] test/test_cgi.py::FormTestCase::testSetIntegerReplaceOne PASSED [ 23%] test/test_cgi.py::FormTestCase::testSetIntegerReplaceZero PASSED [ 23%] test/test_cgi.py::FormTestCase::testSetLink PASSED [ 23%] test/test_cgi.py::FormTestCase::testSetMultilink PASSED [ 23%] test/test_cgi.py::FormTestCase::testSetNumber PASSED [ 23%] test/test_cgi.py::FormTestCase::testSetNumberReplaceNone PASSED [ 23%] test/test_cgi.py::FormTestCase::testSetNumberReplaceOne PASSED [ 23%] test/test_cgi.py::FormTestCase::testSetNumberReplaceZero PASSED [ 23%] test/test_cgi.py::FormTestCase::testSetPassword PASSED [ 23%] test/test_cgi.py::FormTestCase::testSetPasswordConfirmBad PASSED [ 23%] test/test_cgi.py::FormTestCase::testSetString PASSED [ 23%] test/test_cgi.py::FormTestCase::testSingleFileUpload PASSED [ 23%] test/test_cgi.py::FormTestCase::testStringLinkId PASSED [ 23%] test/test_cgi.py::FormTestCase::testStringMultilinkId PASSED [ 24%] test/test_cgi.py::FormTestCase::testUnsetLink PASSED [ 24%] test/test_cgi.py::FormTestCase::testXMLTemplate PASSED [ 24%] test/test_cgi.py::FormTestCase::testXmlrpcCsrfProtection PASSED [ 24%] test/test_cgi.py::FormTestCase::testserve_static_files PASSED [ 24%] test/test_cgi.py::TemplateHtmlRendering::testRenderAltTemplates PASSED [ 24%] test/test_cgi.py::TemplateHtmlRendering::testexamine_url PASSED [ 24%] test/test_cgi.py::TemplateHtmlRendering::testrenderContext PASSED [ 24%] test/test_cgi.py::TemplateHtmlRendering::testrenderFrontPage PASSED [ 24%] test/test_cgi.py::TemplateTestCase::testTemplateSubdirectory PASSED [ 24%] test/test_config.py::ConfigTest::testConfigSave PASSED [ 24%] test/test_config.py::ConfigTest::testFloatAndInt_with_update_option PASSED [ 24%] test/test_config.py::ConfigTest::testIsolationLevel PASSED [ 24%] test/test_config.py::ConfigTest::testLoginAttemptsMin PASSED [ 24%] test/test_config.py::ConfigTest::testStaticFiles PASSED [ 24%] test/test_config.py::ConfigTest::testTimeZone PASSED [ 25%] test/test_config.py::ConfigTest::testTrackerWeb PASSED [ 25%] test/test_config.py::ConfigTest::testWebSecretKey PASSED [ 25%] test/test_config.py::ConfigTest::test_badConfigKeyword PASSED [ 25%] test/test_config.py::ConfigTest::test_validConfigKeyword PASSED [ 25%] test/test_dates.py::DateTestCase::testDate PASSED [ 25%] test/test_dates.py::DateTestCase::testDateError PASSED [ 25%] test/test_dates.py::DateTestCase::testDateInterval PASSED [ 25%] test/test_dates.py::DateTestCase::testDateLocal PASSED [ 25%] test/test_dates.py::DateTestCase::testDateSubtract PASSED [ 25%] test/test_dates.py::DateTestCase::testDivision PASSED [ 25%] test/test_dates.py::DateTestCase::testGranularity PASSED [ 25%] test/test_dates.py::DateTestCase::testIntervalAdd PASSED [ 25%] test/test_dates.py::DateTestCase::testIntervalAddMonthBoundary PASSED [ 25%] test/test_dates.py::DateTestCase::testIntervalAddYearBoundary PASSED [ 26%] test/test_dates.py::DateTestCase::testIntervalInit PASSED [ 26%] test/test_dates.py::DateTestCase::testIntervalInitDate PASSED [ 26%] test/test_dates.py::DateTestCase::testIntervalPretty PASSED [ 26%] test/test_dates.py::DateTestCase::testIntervalSub PASSED [ 26%] test/test_dates.py::DateTestCase::testIntervalSubtractMonthBoundary PASSED [ 26%] test/test_dates.py::DateTestCase::testIntervalSubtractYearBoundary PASSED [ 26%] test/test_dates.py::DateTestCase::testLeapYear PASSED [ 26%] test/test_dates.py::DateTestCase::testOffset PASSED [ 26%] test/test_dates.py::DateTestCase::testOffsetAdd PASSED [ 26%] test/test_dates.py::DateTestCase::testOffsetRandom PASSED [ 26%] test/test_dates.py::DateTestCase::testOffsetSub PASSED [ 26%] test/test_dates.py::DateTestCase::testOverflow PASSED [ 26%] test/test_dates.py::DateTestCase::testPyDatetime PASSED [ 26%] test/test_dates.py::DateTestCase::testSimpleTZ PASSED [ 26%] test/test_dates.py::DateTestCase::testSorting PASSED [ 27%] test/test_dates.py::DateTestCase::testTimestamp PASSED [ 27%] test/test_dates.py::TimezoneTestCase::testTZ PASSED [ 27%] test/test_dates.py::RangeTestCase::testRange PASSED [ 27%] test/test_hyperdbvals.py::RawToHyperdbTest::testBoolean PASSED [ 27%] test/test_hyperdbvals.py::RawToHyperdbTest::testDate PASSED [ 27%] test/test_hyperdbvals.py::RawToHyperdbTest::testInteger PASSED [ 27%] test/test_hyperdbvals.py::RawToHyperdbTest::testInterval PASSED [ 27%] test/test_hyperdbvals.py::RawToHyperdbTest::testLink PASSED [ 27%] test/test_hyperdbvals.py::RawToHyperdbTest::testMultilink PASSED [ 27%] test/test_hyperdbvals.py::RawToHyperdbTest::testMultilink3 PASSED [ 27%] test/test_hyperdbvals.py::RawToHyperdbTest::testNumber PASSED [ 27%] test/test_hyperdbvals.py::RawToHyperdbTest::testPassword PASSED [ 27%] test/test_hyperdbvals.py::RawToHyperdbTest::testString PASSED [ 27%] test/test_indexer.py::IndexerTest::test_basics PASSED [ 27%] test/test_indexer.py::IndexerTest::test_casesensitity PASSED [ 28%] test/test_indexer.py::IndexerTest::test_change PASSED [ 28%] test/test_indexer.py::IndexerTest::test_clear PASSED [ 28%] test/test_indexer.py::IndexerTest::test_extremewords PASSED [ 28%] test/test_indexer.py::IndexerTest::test_manyresults PASSED [ 28%] test/test_indexer.py::IndexerTest::test_stopwords PASSED [ 28%] test/test_indexer.py::IndexerTest::test_unicode PASSED [ 28%] test/test_indexer.py::IndexerTest::test_wordsplitting PASSED [ 28%] test/test_indexer.py::WhooshIndexerTest::test_basics SKIPPED (Skippi...) [ 28%] test/test_indexer.py::WhooshIndexerTest::test_casesensitity SKIPPED [ 28%] test/test_indexer.py::WhooshIndexerTest::test_change SKIPPED (Skippi...) [ 28%] test/test_indexer.py::WhooshIndexerTest::test_clear SKIPPED (Skippin...) [ 28%] test/test_indexer.py::WhooshIndexerTest::test_extremewords SKIPPED (...) [ 28%] test/test_indexer.py::WhooshIndexerTest::test_manyresults SKIPPED (S...) [ 28%] test/test_indexer.py::WhooshIndexerTest::test_stopwords SKIPPED (Ski...) [ 28%] test/test_indexer.py::WhooshIndexerTest::test_unicode SKIPPED (Skipp...) [ 29%] test/test_indexer.py::WhooshIndexerTest::test_wordsplitting SKIPPED [ 29%] test/test_indexer.py::XapianIndexerTest::test_basics SKIPPED (Skippi...) [ 29%] test/test_indexer.py::XapianIndexerTest::test_casesensitity SKIPPED [ 29%] test/test_indexer.py::XapianIndexerTest::test_change SKIPPED (Skippi...) [ 29%] test/test_indexer.py::XapianIndexerTest::test_clear SKIPPED (Skippin...) [ 29%] test/test_indexer.py::XapianIndexerTest::test_extremewords SKIPPED (...) [ 29%] test/test_indexer.py::XapianIndexerTest::test_manyresults SKIPPED (S...) [ 29%] test/test_indexer.py::XapianIndexerTest::test_stopwords SKIPPED (Ski...) [ 29%] test/test_indexer.py::XapianIndexerTest::test_unicode SKIPPED (Skipp...) [ 29%] test/test_indexer.py::XapianIndexerTest::test_wordsplitting SKIPPED [ 29%] test/test_indexer.py::postgresqlIndexerTest::test_basics SKIPPED (Sk...) [ 29%] test/test_indexer.py::postgresqlIndexerTest::test_casesensitity SKIPPED [ 29%] test/test_indexer.py::postgresqlIndexerTest::test_change SKIPPED (Sk...) [ 29%] test/test_indexer.py::postgresqlIndexerTest::test_clear SKIPPED (Ski...) [ 30%] test/test_indexer.py::postgresqlIndexerTest::test_extremewords SKIPPED [ 30%] test/test_indexer.py::postgresqlIndexerTest::test_manyresults SKIPPED [ 30%] test/test_indexer.py::postgresqlIndexerTest::test_stopwords SKIPPED [ 30%] test/test_indexer.py::postgresqlIndexerTest::test_unicode SKIPPED (S...) [ 30%] test/test_indexer.py::postgresqlIndexerTest::test_wordsplitting SKIPPED [ 30%] test/test_indexer.py::mysqlIndexerTest::test_basics SKIPPED (Skippin...) [ 30%] test/test_indexer.py::mysqlIndexerTest::test_casesensitity SKIPPED (...) [ 30%] test/test_indexer.py::mysqlIndexerTest::test_change SKIPPED (Skippin...) [ 30%] test/test_indexer.py::mysqlIndexerTest::test_clear SKIPPED (Skipping...) [ 30%] test/test_indexer.py::mysqlIndexerTest::test_extremewords SKIPPED (S...) [ 30%] test/test_indexer.py::mysqlIndexerTest::test_manyresults SKIPPED (Sk...) [ 30%] test/test_indexer.py::mysqlIndexerTest::test_stopwords SKIPPED (Skip...) [ 30%] test/test_indexer.py::mysqlIndexerTest::test_unicode SKIPPED (Skippi...) [ 30%] test/test_indexer.py::mysqlIndexerTest::test_wordsplitting SKIPPED (...) [ 30%] test/test_indexer.py::sqliteIndexerTest::test_basics PASSED [ 31%] test/test_indexer.py::sqliteIndexerTest::test_casesensitity PASSED [ 31%] test/test_indexer.py::sqliteIndexerTest::test_change PASSED [ 31%] test/test_indexer.py::sqliteIndexerTest::test_clear PASSED [ 31%] test/test_indexer.py::sqliteIndexerTest::test_extremewords PASSED [ 31%] test/test_indexer.py::sqliteIndexerTest::test_manyresults PASSED [ 31%] test/test_indexer.py::sqliteIndexerTest::test_stopwords PASSED [ 31%] test/test_indexer.py::sqliteIndexerTest::test_unicode PASSED [ 31%] test/test_indexer.py::sqliteIndexerTest::test_wordsplitting PASSED [ 31%] test/test_init.py::TemplateInfoTestCase::testLoadTemplateInfo PASSED [ 31%] test/test_jinja2.py::TestCase_Zero::test_zero PASSED [ 31%] test/test_jinja2.py::anydbmJinja2Test::test_zero PASSED [ 31%] test/test_mailer.py::EncodingTestCase::testEncoding PASSED [ 31%] test/test_mailgw.py::MailgwTestCase::testAlternateAddress PASSED [ 31%] test/test_mailgw.py::MailgwTestCase::testAutoReplyEmailsAreIgnored PASSED [ 31%] test/test_mailgw.py::MailgwTestCase::testClassLooseOK PASSED [ 32%] test/test_mailgw.py::MailgwTestCase::testClassStrictInvalid PASSED [ 32%] test/test_mailgw.py::MailgwTestCase::testClassStrictValid PASSED [ 32%] test/test_mailgw.py::MailgwTestCase::testCommandDelimiters PASSED [ 32%] test/test_mailgw.py::MailgwTestCase::testCommandDelimitersIgnore PASSED [ 32%] test/test_mailgw.py::MailgwTestCase::testContentDisposition PASSED [ 32%] test/test_mailgw.py::MailgwTestCase::testDejaVu PASSED [ 32%] test/test_mailgw.py::MailgwTestCase::testDoublePrefixLoose PASSED [ 32%] test/test_mailgw.py::MailgwTestCase::testEmailBodyUnchangedFollowupIsYes PASSED [ 32%] test/test_mailgw.py::MailgwTestCase::testEmailBodyUnchangedNewIsYes PASSED [ 32%] test/test_mailgw.py::MailgwTestCase::testEmailQuoting PASSED [ 32%] test/test_mailgw.py::MailgwTestCase::testEmailQuoting2 PASSED [ 32%] test/test_mailgw.py::MailgwTestCase::testEmailQuotingNewIsFollowup PASSED [ 32%] test/test_mailgw.py::MailgwTestCase::testEmailQuotingNewIsNew PASSED [ 32%] test/test_mailgw.py::MailgwTestCase::testEmailQuotingRemove PASSED [ 32%] test/test_mailgw.py::MailgwTestCase::testEmailQuotingRemove2 PASSED [ 33%] test/test_mailgw.py::MailgwTestCase::testEmailQuotingRemove3 PASSED [ 33%] test/test_mailgw.py::MailgwTestCase::testEmailReplaceBodyNewIsFollowup PASSED [ 33%] test/test_mailgw.py::MailgwTestCase::testEmailReplaceBodyNewIsNew PASSED [ 33%] test/test_mailgw.py::MailgwTestCase::testEmptyMessage PASSED [ 33%] test/test_mailgw.py::MailgwTestCase::testEnc01 PASSED [ 33%] test/test_mailgw.py::MailgwTestCase::testEncNonUTF8 PASSED [ 33%] test/test_mailgw.py::MailgwTestCase::testFollowup PASSED [ 33%] test/test_mailgw.py::MailgwTestCase::testFollowupEmptyMessage PASSED [ 33%] test/test_mailgw.py::MailgwTestCase::testFollowupEmptyMessageNoSubject PASSED [ 33%] test/test_mailgw.py::MailgwTestCase::testFollowupExplicitSubjectChange PASSED [ 33%] test/test_mailgw.py::MailgwTestCase::testFollowupNoNosyAuthor PASSED [ 33%] test/test_mailgw.py::MailgwTestCase::testFollowupNoNosyAuthorButCopy PASSED [ 33%] test/test_mailgw.py::MailgwTestCase::testFollowupNoNosyAuthorNoCopy PASSED [ 33%] test/test_mailgw.py::MailgwTestCase::testFollowupNoNosyRecipients PASSED [ 34%] test/test_mailgw.py::MailgwTestCase::testFollowupNoSubjectChange PASSED [ 34%] test/test_mailgw.py::MailgwTestCase::testFollowupNosyAuthor PASSED [ 34%] test/test_mailgw.py::MailgwTestCase::testFollowupNosyAuthorAndCopy PASSED [ 34%] test/test_mailgw.py::MailgwTestCase::testFollowupNosyAuthorNosyCopy PASSED [ 34%] test/test_mailgw.py::MailgwTestCase::testFollowupNosyRecipients PASSED [ 34%] test/test_mailgw.py::MailgwTestCase::testFollowupOnNonIssue PASSED [ 34%] test/test_mailgw.py::MailgwTestCase::testFollowupStupidQuoting PASSED [ 34%] test/test_mailgw.py::MailgwTestCase::testFollowupTitleMatch PASSED [ 34%] test/test_mailgw.py::MailgwTestCase::testFollowupTitleMatchInterval PASSED [ 34%] test/test_mailgw.py::MailgwTestCase::testFollowupTitleMatchMultiRe PASSED [ 34%] test/test_mailgw.py::MailgwTestCase::testFollowupTitleMatchNever PASSED [ 34%] test/test_mailgw.py::MailgwTestCase::testFollowupTitleMatchNeverInterval PASSED [ 34%] test/test_mailgw.py::MailgwTestCase::testFollowupUTF8 PASSED [ 34%] test/test_mailgw.py::MailgwTestCase::testForwardedMessageAttachment PASSED [ 34%] test/test_mailgw.py::MailgwTestCase::testHelpMessage PASSED [ 35%] test/test_mailgw.py::MailgwTestCase::testHelpSubject PASSED [ 35%] test/test_mailgw.py::MailgwTestCase::testInvalidClassLoose PASSED [ 35%] test/test_mailgw.py::MailgwTestCase::testInvalidClassLooseReply PASSED [ 35%] test/test_mailgw.py::MailgwTestCase::testInvalidClassLooseReplyQuoted PASSED [ 35%] test/test_mailgw.py::MailgwTestCase::testInvalidCommandPassthrough PASSED [ 35%] test/test_mailgw.py::MailgwTestCase::testInvalidCommandPassthroughLoose PASSED [ 35%] test/test_mailgw.py::MailgwTestCase::testInvalidCommandPassthroughLooseOK PASSED [ 35%] test/test_mailgw.py::MailgwTestCase::testInvalidCommands PASSED [ 35%] test/test_mailgw.py::MailgwTestCase::testInvalidDesignator PASSED [ 35%] test/test_mailgw.py::MailgwTestCase::testIssueidLast PASSED [ 35%] test/test_mailgw.py::MailgwTestCase::testItsBulkStupid PASSED [ 35%] test/test_mailgw.py::MailgwTestCase::testMaillistSubject PASSED [ 35%] test/test_mailgw.py::MailgwTestCase::testMessageWithFromInIt PASSED [ 35%] test/test_mailgw.py::MailgwTestCase::testMultipartCharsetLatin1AttachFile PASSED [ 35%] test/test_mailgw.py::MailgwTestCase::testMultipartCharsetLatin1NoAttach PASSED [ 36%] test/test_mailgw.py::MailgwTestCase::testMultipartCharsetUTF8AttachFile PASSED [ 36%] test/test_mailgw.py::MailgwTestCase::testMultipartCharsetUTF8NoAttach PASSED [ 36%] test/test_mailgw.py::MailgwTestCase::testMultipartDropAlternatives PASSED [ 36%] test/test_mailgw.py::MailgwTestCase::testMultipartEnc01 PASSED [ 36%] test/test_mailgw.py::MailgwTestCase::testMultipartKeepAlternatives PASSED [ 36%] test/test_mailgw.py::MailgwTestCase::testMultipartKeepFiles PASSED [ 36%] test/test_mailgw.py::MailgwTestCase::testMultipartRFC822 PASSED [ 36%] test/test_mailgw.py::MailgwTestCase::testMultipartRFC822Unpack PASSED [ 36%] test/test_mailgw.py::MailgwTestCase::testMultipartSeveralAttachmentMessages PASSED [ 36%] test/test_mailgw.py::MailgwTestCase::testMultipartTextifyHTML PASSED [ 36%] test/test_mailgw.py::MailgwTestCase::testNewIssue PASSED [ 36%] test/test_mailgw.py::MailgwTestCase::testNewIssueAuthMsg PASSED [ 36%] test/test_mailgw.py::MailgwTestCase::testNewIssueNoAuthorEmail PASSED [ 36%] test/test_mailgw.py::MailgwTestCase::testNewIssueNoAuthorInfo PASSED [ 36%] test/test_mailgw.py::MailgwTestCase::testNewIssueNoClass PASSED [ 37%] test/test_mailgw.py::MailgwTestCase::testNewIssueNosy PASSED [ 37%] test/test_mailgw.py::MailgwTestCase::testNewIssueNosyAuthor PASSED [ 37%] test/test_mailgw.py::MailgwTestCase::testNewUserAuthor PASSED [ 37%] test/test_mailgw.py::MailgwTestCase::testNewUserAuthorEncodedName PASSED [ 37%] test/test_mailgw.py::MailgwTestCase::testNewUserAuthorMixedEncodedName PASSED [ 37%] test/test_mailgw.py::MailgwTestCase::testNewUserAuthorMixedEncodedNameSpacing PASSED [ 37%] test/test_mailgw.py::MailgwTestCase::testNoMessageId PASSED [ 37%] test/test_mailgw.py::MailgwTestCase::testNoSubject PASSED [ 37%] test/test_mailgw.py::MailgwTestCase::testNosyGeneration PASSED [ 37%] test/test_mailgw.py::MailgwTestCase::testNosyMessageCcBccEtc PASSED [ 37%] test/test_mailgw.py::MailgwTestCase::testNosyMessageNoteFilter PASSED [ 37%] test/test_mailgw.py::MailgwTestCase::testNosyMessageSettingSubject PASSED [ 37%] test/test_mailgw.py::MailgwTestCase::testNosyRemove PASSED [ 37%] test/test_mailgw.py::MailgwTestCase::testNosyReplytoAuthor PASSED [ 38%] test/test_mailgw.py::MailgwTestCase::testNosyReplytoSomeaddress PASSED [ 38%] test/test_mailgw.py::MailgwTestCase::testNosyReplytoTracker PASSED [ 38%] test/test_mailgw.py::MailgwTestCase::testOctetStreamTranscoding PASSED [ 38%] test/test_mailgw.py::MailgwTestCase::testOneCharSubject PASSED [ 38%] test/test_mailgw.py::MailgwTestCase::testOptionClass PASSED [ 38%] test/test_mailgw.py::MailgwTestCase::testOptions PASSED [ 38%] test/test_mailgw.py::MailgwTestCase::testOptionsMulti PASSED [ 38%] test/test_mailgw.py::MailgwTestCase::testOutlookAttachment PASSED [ 38%] test/test_mailgw.py::MailgwTestCase::testPrefixDelimiters PASSED [ 38%] test/test_mailgw.py::MailgwTestCase::testPropertyChangeOnly PASSED [ 38%] test/test_mailgw.py::MailgwTestCase::testRegistrationConfirmation PASSED [ 38%] test/test_mailgw.py::MailgwTestCase::testReplytoMatch PASSED [ 38%] test/test_mailgw.py::MailgwTestCase::testResentFrom PASSED [ 38%] test/test_mailgw.py::MailgwTestCase::testResentFromSwitchedOff PASSED [ 38%] test/test_mailgw.py::MailgwTestCase::testSecurityMessagePermissionContent PASSED [ 39%] test/test_mailgw.py::MailgwTestCase::testSimpleFollowup PASSED [ 39%] test/test_mailgw.py::MailgwTestCase::testSpacesAroundMultilinkPropertyValue PASSED [ 39%] test/test_mailgw.py::MailgwTestCase::testStandardMsg PASSED [ 39%] test/test_mailgw.py::MailgwTestCase::testTextHtmlMessage PASSED [ 39%] test/test_mailgw.py::MailgwTestCase::testUnknownPrefixSubject PASSED [ 39%] test/test_mailgw.py::MailgwTestCase::testUnknownUser PASSED [ 39%] test/test_mailgw.py::MailgwTestCase::testUserAlternateLookup PASSED [ 39%] test/test_mailgw.py::MailgwTestCase::testUserAlternateSubstringNomatch PASSED [ 39%] test/test_mailgw.py::MailgwTestCase::testUserCreate PASSED [ 39%] test/test_mailgw.py::MailgwTestCase::testUserLookup PASSED [ 39%] test/test_mailgw.py::MailgwTestCase::testmsgHeaderPropertyAssignedto PASSED [ 39%] test/test_mailgw.py::MailgwTestCase::testmsgHeaderPropertyEmptyString PASSED [ 39%] test/test_mailgw.py::MailgwPGPTestCase::testEmptyMessage SKIPPED (Sk...) [ 39%] test/test_mailgw.py::MailgwPGPTestCase::testHelpMessage SKIPPED (Ski...) [ 39%] test/test_mailgw.py::MailgwPGPTestCase::testPGPEncryptedSignedMessage SKIPPED [ 40%] test/test_mailgw.py::MailgwPGPTestCase::testPGPEncryptedUnsignedMessage SKIPPED [ 40%] test/test_mailgw.py::MailgwPGPTestCase::testPGPEncryptedUnsignedMessageCheckBounce SKIPPED [ 40%] test/test_mailgw.py::MailgwPGPTestCase::testPGPEncryptedUnsignedMessageError SKIPPED [ 40%] test/test_mailgw.py::MailgwPGPTestCase::testPGPEncryptedUnsignedMessageFromNonPGPUser SKIPPED [ 40%] test/test_mailgw.py::MailgwPGPTestCase::testPGPSignedMessage SKIPPED [ 40%] test/test_mailgw.py::MailgwPGPTestCase::testPGPSignedMessageFail SKIPPED [ 40%] test/test_mailgw.py::MailgwPGPTestCase::testPGPUnsignedMessage SKIPPED [ 40%] test/test_mailgw_roundupmessage.py::FlattenRoundupMessageTests::test_flatten_with_from PASSED [ 40%] test/test_mailgw_roundupmessage.py::HeaderRoundupMessageTests::test_get_address_list PASSED [ 40%] test/test_mailgw_roundupmessage.py::HeaderRoundupMessageTests::test_get_encoded_header PASSED [ 40%] test/test_mailgw_roundupmessage.py::HeaderRoundupMessageTests::test_get_plain_header PASSED [ 40%] test/test_mailgw_roundupmessage.py::BodyRoundupMessageTests::test_get_body_base64 PASSED [ 40%] test/test_mailgw_roundupmessage.py::BodyRoundupMessageTests::test_get_body_iso_8859_1 PASSED [ 40%] test/test_mailgw_roundupmessage.py::BodyRoundupMessageTests::test_get_body_utf_8 PASSED [ 41%] test/test_mailgw_roundupmessage.py::AsAttachmentRoundupMessageTests::test_octet_stream PASSED [ 41%] test/test_mailgw_roundupmessage.py::AsAttachmentRoundupMessageTests::test_rfc822 PASSED [ 41%] test/test_mailgw_roundupmessage.py::AsAttachmentRoundupMessageTests::test_rfc822_no_payload PASSED [ 41%] test/test_mailgw_roundupmessage.py::AsAttachmentRoundupMessageTests::test_rfc822_no_subject PASSED [ 41%] test/test_mailgw_roundupmessage.py::AsAttachmentRoundupMessageTests::test_text_plain PASSED [ 41%] test/test_mailgw_roundupmessage.py::ExtractContentRoundupMessageTests::test_attached_signature PASSED [ 41%] test/test_mailgw_roundupmessage.py::ExtractContentRoundupMessageTests::test_attached_text_plain PASSED [ 41%] test/test_mailgw_roundupmessage.py::ExtractContentRoundupMessageTests::test_deep_multipart_alternative PASSED [ 41%] test/test_mailgw_roundupmessage.py::ExtractContentRoundupMessageTests::test_deep_multipart_alternative_ignore PASSED [ 41%] test/test_mailgw_roundupmessage.py::ExtractContentRoundupMessageTests::test_multipart_alternative PASSED [ 41%] test/test_mailgw_roundupmessage.py::ExtractContentRoundupMessageTests::test_multipart_mixed PASSED [ 41%] test/test_mailgw_roundupmessage.py::ExtractContentRoundupMessageTests::test_rfc822_message PASSED [ 41%] test/test_mailgw_roundupmessage.py::ExtractContentRoundupMessageTests::test_rfc822_message_unpack PASSED [ 41%] test/test_mailgw_roundupmessage.py::ExtractContentRoundupMessageTests::test_signed_attachemts PASSED [ 41%] test/test_mailgw_roundupmessage.py::ExtractContentRoundupMessageTests::test_signed_text PASSED [ 42%] test/test_mailgw_roundupmessage.py::ExtractContentRoundupMessageTests::test_text_plain PASSED [ 42%] test/test_mailgw_roundupmessage.py::PgpDetectRoundupMessageTests::test_pgp_message_encrypted PASSED [ 42%] test/test_mailgw_roundupmessage.py::PgpDetectRoundupMessageTests::test_pgp_message_encrypted_protocol_invalid PASSED [ 42%] test/test_mailgw_roundupmessage.py::PgpDetectRoundupMessageTests::test_pgp_message_encrypted_protocol_missing PASSED [ 42%] test/test_mailgw_roundupmessage.py::PgpDetectRoundupMessageTests::test_pgp_message_not_encrypted PASSED [ 42%] test/test_mailgw_roundupmessage.py::PgpDetectRoundupMessageTests::test_pgp_message_not_signed PASSED [ 42%] test/test_mailgw_roundupmessage.py::PgpDetectRoundupMessageTests::test_pgp_message_signed PASSED [ 42%] test/test_mailgw_roundupmessage.py::PgpDetectRoundupMessageTests::test_pgp_message_signed_protocol_invalid PASSED [ 42%] test/test_mailgw_roundupmessage.py::PgpDetectRoundupMessageTests::test_pgp_message_signed_protocol_missing PASSED [ 42%] test/test_mailsplit.py::MailsplitTestCase::testAllQuoted PASSED [ 42%] test/test_mailsplit.py::MailsplitTestCase::testEmpty PASSED [ 42%] test/test_mailsplit.py::MailsplitTestCase::testIndentationContent PASSED [ 42%] test/test_mailsplit.py::MailsplitTestCase::testIndentationSummary PASSED [ 42%] test/test_mailsplit.py::MailsplitTestCase::testKeepBody PASSED [ 42%] test/test_mailsplit.py::MailsplitTestCase::testKeepCitation PASSED [ 43%] test/test_mailsplit.py::MailsplitTestCase::testKeepMultipleHyphens PASSED [ 43%] test/test_mailsplit.py::MailsplitTestCase::testMultilineSummary PASSED [ 43%] test/test_mailsplit.py::MailsplitTestCase::testParagraphs PASSED [ 43%] test/test_mailsplit.py::MailsplitTestCase::testPostComment PASSED [ 43%] test/test_mailsplit.py::MailsplitTestCase::testPreComment PASSED [ 43%] test/test_mailsplit.py::MailsplitTestCase::testSimple PASSED [ 43%] test/test_mailsplit.py::MailsplitTestCase::testSimpleFollowup PASSED [ 43%] test/test_mailsplit.py::MailsplitTestCase::testSimpleFollowupParas PASSED [ 43%] test/test_memorydb.py::memorydbDBTest::testActorProperty <- test/db_test_base.py PASSED [ 43%] test/test_memorydb.py::memorydbDBTest::testAddProperty <- test/db_test_base.py PASSED [ 43%] test/test_memorydb.py::memorydbDBTest::testAddRemoveProperty <- test/db_test_base.py PASSED [ 43%] test/test_memorydb.py::memorydbDBTest::testAdminDuplicateInitialisation <- test/db_test_base.py PASSED [ 43%] test/test_memorydb.py::memorydbDBTest::testAdminImportExport <- test/db_test_base.py PASSED [ 43%] test/test_memorydb.py::memorydbDBTest::testAdminOtherCommands <- test/db_test_base.py PASSED [ 43%] test/test_memorydb.py::memorydbDBTest::testAuditorTwo <- test/db_test_base.py PASSED [ 44%] test/test_memorydb.py::memorydbDBTest::testAuditors <- test/db_test_base.py PASSED [ 44%] test/test_memorydb.py::memorydbDBTest::testBooleanChange <- test/db_test_base.py PASSED [ 44%] test/test_memorydb.py::memorydbDBTest::testBooleanSet <- test/db_test_base.py PASSED [ 44%] test/test_memorydb.py::memorydbDBTest::testBooleanUnset <- test/db_test_base.py PASSED [ 44%] test/test_memorydb.py::memorydbDBTest::testCacheCreateSet <- test/db_test_base.py PASSED [ 44%] test/test_memorydb.py::memorydbDBTest::testCreatorProperty <- test/db_test_base.py PASSED [ 44%] test/test_memorydb.py::memorydbDBTest::testDateChange <- test/db_test_base.py PASSED [ 44%] test/test_memorydb.py::memorydbDBTest::testDateLeapYear <- test/db_test_base.py PASSED [ 44%] test/test_memorydb.py::memorydbDBTest::testDateSort <- test/db_test_base.py PASSED [ 44%] test/test_memorydb.py::memorydbDBTest::testDateSortMultilink <- test/db_test_base.py PASSED [ 44%] test/test_memorydb.py::memorydbDBTest::testDateUnset <- test/db_test_base.py PASSED [ 44%] test/test_memorydb.py::memorydbDBTest::testDefault_Value <- test/db_test_base.py PASSED [ 44%] test/test_memorydb.py::memorydbDBTest::testDestroyBlob <- test/db_test_base.py PASSED [ 44%] test/test_memorydb.py::memorydbDBTest::testDestroyJournalling <- test/db_test_base.py PASSED [ 45%] test/test_memorydb.py::memorydbDBTest::testDestroyNoJournalling <- test/db_test_base.py PASSED [ 45%] test/test_memorydb.py::memorydbDBTest::testDoubleChange <- test/db_test_base.py PASSED [ 45%] test/test_memorydb.py::memorydbDBTest::testDoubleUnset <- test/db_test_base.py PASSED [ 45%] test/test_memorydb.py::memorydbDBTest::testEmptySet <- test/db_test_base.py PASSED [ 45%] test/test_memorydb.py::memorydbDBTest::testExceptions <- test/db_test_base.py PASSED [ 45%] test/test_memorydb.py::memorydbDBTest::testFileClassContentChange <- test/db_test_base.py PASSED [ 45%] test/test_memorydb.py::memorydbDBTest::testFileClassIndexingNoNoNo <- test/db_test_base.py PASSED [ 45%] test/test_memorydb.py::memorydbDBTest::testFileClassReindexing <- test/db_test_base.py PASSED [ 45%] test/test_memorydb.py::memorydbDBTest::testFilteringBoolean <- test/db_test_base.py PASSED [ 45%] test/test_memorydb.py::memorydbDBTest::testFilteringDateRangeMulti <- test/db_test_base.py PASSED [ 45%] test/test_memorydb.py::memorydbDBTest::testFilteringDateSort <- test/db_test_base.py PASSED [ 45%] test/test_memorydb.py::memorydbDBTest::testFilteringDateSortPriorityGroup <- test/db_test_base.py PASSED [ 45%] test/test_memorydb.py::memorydbDBTest::testFilteringID <- test/db_test_base.py PASSED [ 45%] test/test_memorydb.py::memorydbDBTest::testFilteringIntervalSort <- test/db_test_base.py PASSED [ 45%] test/test_memorydb.py::memorydbDBTest::testFilteringLink <- test/db_test_base.py PASSED [ 46%] test/test_memorydb.py::memorydbDBTest::testFilteringLinkSortGroup <- test/db_test_base.py PASSED [ 46%] test/test_memorydb.py::memorydbDBTest::testFilteringLinkSortSearchMultilink <- test/db_test_base.py PASSED [ 46%] test/test_memorydb.py::memorydbDBTest::testFilteringMany <- test/db_test_base.py PASSED [ 46%] test/test_memorydb.py::memorydbDBTest::testFilteringMultilink <- test/db_test_base.py PASSED [ 46%] test/test_memorydb.py::memorydbDBTest::testFilteringMultilinkAndGroup <- test/db_test_base.py PASSED [ 46%] test/test_memorydb.py::memorydbDBTest::testFilteringMultilinkSort <- test/db_test_base.py PASSED [ 46%] test/test_memorydb.py::memorydbDBTest::testFilteringMultilinkSortGroup <- test/db_test_base.py PASSED [ 46%] test/test_memorydb.py::memorydbDBTest::testFilteringNumber <- test/db_test_base.py PASSED [ 46%] test/test_memorydb.py::memorydbDBTest::testFilteringRangeBasic <- test/db_test_base.py PASSED [ 46%] test/test_memorydb.py::memorydbDBTest::testFilteringRangeGeekInterval <- test/db_test_base.py PASSED [ 46%] test/test_memorydb.py::memorydbDBTest::testFilteringRangeInterval <- test/db_test_base.py PASSED [ 46%] test/test_memorydb.py::memorydbDBTest::testFilteringRangeMonths <- test/db_test_base.py PASSED [ 46%] test/test_memorydb.py::memorydbDBTest::testFilteringRangeTwoSyntaxes <- test/db_test_base.py PASSED [ 46%] test/test_memorydb.py::memorydbDBTest::testFilteringRangeYearMonthDay <- test/db_test_base.py PASSED [ 46%] test/test_memorydb.py::memorydbDBTest::testFilteringRetired <- test/db_test_base.py PASSED [ 47%] test/test_memorydb.py::memorydbDBTest::testFilteringRetiredString <- test/db_test_base.py PASSED [ 47%] test/test_memorydb.py::memorydbDBTest::testFilteringRevLink <- test/db_test_base.py PASSED [ 47%] test/test_memorydb.py::memorydbDBTest::testFilteringRevMultilink <- test/db_test_base.py PASSED [ 47%] test/test_memorydb.py::memorydbDBTest::testFilteringSortId <- test/db_test_base.py PASSED [ 47%] test/test_memorydb.py::memorydbDBTest::testFilteringSpecialChars <- test/db_test_base.py PASSED [ 47%] test/test_memorydb.py::memorydbDBTest::testFilteringString <- test/db_test_base.py PASSED [ 47%] test/test_memorydb.py::memorydbDBTest::testFilteringStringCase <- test/db_test_base.py PASSED [ 47%] test/test_memorydb.py::memorydbDBTest::testFilteringStringExactMatch <- test/db_test_base.py PASSED [ 47%] test/test_memorydb.py::memorydbDBTest::testFilteringStringSort <- test/db_test_base.py PASSED [ 47%] test/test_memorydb.py::memorydbDBTest::testFilteringTransitiveLinkIssue <- test/db_test_base.py PASSED [ 47%] test/test_memorydb.py::memorydbDBTest::testFilteringTransitiveLinkSort <- test/db_test_base.py PASSED [ 47%] test/test_memorydb.py::memorydbDBTest::testFilteringTransitiveLinkSortNull <- test/db_test_base.py PASSED [ 47%] test/test_memorydb.py::memorydbDBTest::testFilteringTransitiveLinkUser <- test/db_test_base.py PASSED [ 47%] test/test_memorydb.py::memorydbDBTest::testFilteringTransitiveLinkUserLimit <- test/db_test_base.py PASSED [ 47%] test/test_memorydb.py::memorydbDBTest::testFilteringTransitiveMultilink <- test/db_test_base.py PASSED [ 48%] test/test_memorydb.py::memorydbDBTest::testFilteringTransitiveMultilinkSort <- test/db_test_base.py PASSED [ 48%] test/test_memorydb.py::memorydbDBTest::testFindIncorrectProperty <- test/db_test_base.py PASSED [ 48%] test/test_memorydb.py::memorydbDBTest::testFindLink <- test/db_test_base.py PASSED [ 48%] test/test_memorydb.py::memorydbDBTest::testFindLinkAndMultilink <- test/db_test_base.py PASSED [ 48%] test/test_memorydb.py::memorydbDBTest::testFindLinkFail <- test/db_test_base.py PASSED [ 48%] test/test_memorydb.py::memorydbDBTest::testFindLinkUnset <- test/db_test_base.py PASSED [ 48%] test/test_memorydb.py::memorydbDBTest::testFindMultiMultilink <- test/db_test_base.py PASSED [ 48%] test/test_memorydb.py::memorydbDBTest::testFindMultilink <- test/db_test_base.py PASSED [ 48%] test/test_memorydb.py::memorydbDBTest::testFindMultilinkFail <- test/db_test_base.py PASSED [ 48%] test/test_memorydb.py::memorydbDBTest::testFindMultilinkUnset <- test/db_test_base.py PASSED [ 48%] test/test_memorydb.py::memorydbDBTest::testFindMultipleLink <- test/db_test_base.py PASSED [ 48%] test/test_memorydb.py::memorydbDBTest::testFindRetired <- test/db_test_base.py PASSED [ 48%] test/test_memorydb.py::memorydbDBTest::testFindRevLinkMultilink <- test/db_test_base.py PASSED [ 48%] test/test_memorydb.py::memorydbDBTest::testForcedReindexing <- test/db_test_base.py PASSED [ 49%] test/test_memorydb.py::memorydbDBTest::testIDGeneration <- test/db_test_base.py PASSED [ 49%] test/test_memorydb.py::memorydbDBTest::testIDSetting <- test/db_test_base.py PASSED [ 49%] test/test_memorydb.py::memorydbDBTest::testImportExport <- test/db_test_base.py PASSED [ 49%] test/test_memorydb.py::memorydbDBTest::testIndexerSearchMulti <- test/db_test_base.py PASSED [ 49%] test/test_memorydb.py::memorydbDBTest::testIndexerSearching <- test/db_test_base.py PASSED [ 49%] test/test_memorydb.py::memorydbDBTest::testIndexerSearchingLink <- test/db_test_base.py PASSED [ 49%] test/test_memorydb.py::memorydbDBTest::testIndexingPropertiesOnImport <- test/db_test_base.py PASSED [ 49%] test/test_memorydb.py::memorydbDBTest::testIntegerChange <- test/db_test_base.py PASSED [ 49%] test/test_memorydb.py::memorydbDBTest::testIntegerUnset <- test/db_test_base.py PASSED [ 49%] test/test_memorydb.py::memorydbDBTest::testIntervalChange <- test/db_test_base.py PASSED [ 49%] test/test_memorydb.py::memorydbDBTest::testIntervalUnset <- test/db_test_base.py PASSED [ 49%] test/test_memorydb.py::memorydbDBTest::testJournalNonexistingProperty <- test/db_test_base.py PASSED [ 49%] test/test_memorydb.py::memorydbDBTest::testJournalPreCommit <- test/db_test_base.py PASSED [ 49%] test/test_memorydb.py::memorydbDBTest::testJournals <- test/db_test_base.py PASSED [ 49%] test/test_memorydb.py::memorydbDBTest::testKeyValue <- test/db_test_base.py PASSED [ 50%] test/test_memorydb.py::memorydbDBTest::testLabelProp <- test/db_test_base.py PASSED [ 50%] test/test_memorydb.py::memorydbDBTest::testLinkChange <- test/db_test_base.py PASSED [ 50%] test/test_memorydb.py::memorydbDBTest::testLinkUnset <- test/db_test_base.py PASSED [ 50%] test/test_memorydb.py::memorydbDBTest::testMakeSeveralMultilinkedNodes <- test/db_test_base.py PASSED [ 50%] test/test_memorydb.py::memorydbDBTest::testMultilinkChange <- test/db_test_base.py PASSED [ 50%] test/test_memorydb.py::memorydbDBTest::testMultilinkChangeIterable <- test/db_test_base.py PASSED [ 50%] test/test_memorydb.py::memorydbDBTest::testNosyMail <- test/db_test_base.py PASSED [ 50%] test/test_memorydb.py::memorydbDBTest::testNosyMailTextAndBinary <- test/db_test_base.py PASSED [ 50%] test/test_memorydb.py::memorydbDBTest::testNumberChange <- test/db_test_base.py PASSED [ 50%] test/test_memorydb.py::memorydbDBTest::testNumberUnset <- test/db_test_base.py PASSED [ 50%] test/test_memorydb.py::memorydbDBTest::testPGPNosyMail <- test/db_test_base.py SKIPPED [ 50%] test/test_memorydb.py::memorydbDBTest::testPack <- test/db_test_base.py PASSED [ 50%] test/test_memorydb.py::memorydbDBTest::testPasswordChange <- test/db_test_base.py PASSED [ 50%] test/test_memorydb.py::memorydbDBTest::testPasswordUnset <- test/db_test_base.py PASSED [ 50%] test/test_memorydb.py::memorydbDBTest::testQuietChangenote <- test/db_test_base.py PASSED [ 51%] test/test_memorydb.py::memorydbDBTest::testQuietJournal <- test/db_test_base.py PASSED [ 51%] test/test_memorydb.py::memorydbDBTest::testQuietProperty <- test/db_test_base.py PASSED [ 51%] test/test_memorydb.py::memorydbDBTest::testRefresh <- test/db_test_base.py PASSED [ 51%] test/test_memorydb.py::memorydbDBTest::testReindexingChange <- test/db_test_base.py PASSED [ 51%] test/test_memorydb.py::memorydbDBTest::testReindexingClear <- test/db_test_base.py PASSED [ 51%] test/test_memorydb.py::memorydbDBTest::testRemoveProperty <- test/db_test_base.py PASSED [ 51%] test/test_memorydb.py::memorydbDBTest::testRetire <- test/db_test_base.py PASSED [ 51%] test/test_memorydb.py::memorydbDBTest::testSerialisation <- test/db_test_base.py PASSED [ 51%] test/test_memorydb.py::memorydbDBTest::testStringBinary <- test/db_test_base.py PASSED [ 51%] test/test_memorydb.py::memorydbDBTest::testStringChange <- test/db_test_base.py PASSED [ 51%] test/test_memorydb.py::memorydbDBTest::testStringFind <- test/db_test_base.py PASSED [ 51%] test/test_memorydb.py::memorydbDBTest::testStringUnicode <- test/db_test_base.py PASSED [ 51%] test/test_memorydb.py::memorydbDBTest::testStringUnset <- test/db_test_base.py PASSED [ 51%] test/test_memorydb.py::memorydbDBTest::testTransactions <- test/db_test_base.py PASSED [ 52%] test/test_memorydb.py::memorydbDBTest::testViewPremJournal <- test/db_test_base.py PASSED [ 52%] test/test_memorydb.py::memorydbROTest::testExceptions <- test/db_test_base.py PASSED [ 52%] test/test_memorydb.py::memorydbSchemaTest::test_addNewClass <- test/db_test_base.py PASSED [ 52%] test/test_memorydb.py::memorydbSchemaTest::test_changeClassKey <- test/db_test_base.py PASSED [ 52%] test/test_memorydb.py::memorydbSchemaTest::test_fileClassProps <- test/db_test_base.py PASSED [ 52%] test/test_memorydb.py::memorydbSchemaTest::test_makeNewMultilink <- test/db_test_base.py PASSED [ 52%] test/test_memorydb.py::memorydbSchemaTest::test_modifyClass <- test/db_test_base.py PASSED [ 52%] test/test_memorydb.py::memorydbSchemaTest::test_removeClass <- test/db_test_base.py PASSED [ 52%] test/test_memorydb.py::memorydbSchemaTest::test_removeClassKey <- test/db_test_base.py PASSED [ 52%] test/test_memorydb.py::memorydbSchemaTest::test_removeMultilink <- test/db_test_base.py PASSED [ 52%] test/test_memorydb.py::memorydbSchemaTest::test_reservedProperties <- test/db_test_base.py PASSED [ 52%] test/test_memorydb.py::memorydbSessionTest::testDestroy <- test/session_common.py PASSED [ 52%] test/test_memorydb.py::memorydbSessionTest::testGetAll <- test/session_common.py PASSED [ 52%] test/test_memorydb.py::memorydbSessionTest::testList <- test/session_common.py PASSED [ 52%] test/test_memorydb.py::memorydbSessionTest::testSetSession <- test/session_common.py PASSED [ 53%] test/test_memorydb.py::memorydbSessionTest::testUpdateSession <- test/session_common.py PASSED [ 53%] test/test_misc.py::AcceptLanguageTest::testParse PASSED [ 53%] test/test_misc.py::CmpTest::testCmp PASSED [ 53%] test/test_multipart.py::MultipartTestCase::testAttachedSignature PASSED [ 53%] test/test_multipart.py::MultipartTestCase::testAttachedTextPlain PASSED [ 53%] test/test_multipart.py::MultipartTestCase::testDeepMultipartAlternative PASSED [ 53%] test/test_multipart.py::MultipartTestCase::testMessageRfc822 PASSED [ 53%] test/test_multipart.py::MultipartTestCase::testMultipart PASSED [ 53%] test/test_multipart.py::MultipartTestCase::testMultipartAlternative PASSED [ 53%] test/test_multipart.py::MultipartTestCase::testMultipartAlternativeHtml PASSED [ 53%] test/test_multipart.py::MultipartTestCase::testMultipartAlternativeHtmlText PASSED [ 53%] test/test_multipart.py::MultipartTestCase::testMultipartMixed PASSED [ 53%] test/test_multipart.py::MultipartTestCase::testMultipartMixedHtml PASSED [ 53%] test/test_multipart.py::MultipartTestCase::testSignedAttachments PASSED [ 53%] test/test_multipart.py::MultipartTestCase::testSignedText PASSED [ 54%] test/test_multipart.py::MultipartTestCase::testTextPlain PASSED [ 54%] test/test_mysql.py::mysqlDBTest::testActorProperty <- test/db_test_base.py SKIPPED [ 54%] test/test_mysql.py::mysqlDBTest::testAddProperty <- test/db_test_base.py SKIPPED [ 54%] test/test_mysql.py::mysqlDBTest::testAddRemoveProperty <- test/db_test_base.py SKIPPED [ 54%] test/test_mysql.py::mysqlDBTest::testAdminDuplicateInitialisation <- test/db_test_base.py SKIPPED [ 54%] test/test_mysql.py::mysqlDBTest::testAdminImportExport <- test/db_test_base.py SKIPPED [ 54%] test/test_mysql.py::mysqlDBTest::testAdminOtherCommands <- test/db_test_base.py SKIPPED [ 54%] test/test_mysql.py::mysqlDBTest::testAuditorTwo <- test/db_test_base.py SKIPPED [ 54%] test/test_mysql.py::mysqlDBTest::testAuditors <- test/db_test_base.py SKIPPED [ 54%] test/test_mysql.py::mysqlDBTest::testBooleanChange <- test/db_test_base.py SKIPPED [ 54%] test/test_mysql.py::mysqlDBTest::testBooleanSet <- test/db_test_base.py SKIPPED [ 54%] test/test_mysql.py::mysqlDBTest::testBooleanUnset <- test/db_test_base.py SKIPPED [ 54%] test/test_mysql.py::mysqlDBTest::testCacheCreateSet <- test/db_test_base.py SKIPPED [ 54%] test/test_mysql.py::mysqlDBTest::testCreatorProperty <- test/db_test_base.py SKIPPED [ 54%] test/test_mysql.py::mysqlDBTest::testDateChange <- test/db_test_base.py SKIPPED [ 55%] test/test_mysql.py::mysqlDBTest::testDateLeapYear <- test/db_test_base.py SKIPPED [ 55%] test/test_mysql.py::mysqlDBTest::testDateSort <- test/db_test_base.py SKIPPED [ 55%] test/test_mysql.py::mysqlDBTest::testDateSortMultilink <- test/db_test_base.py SKIPPED [ 55%] test/test_mysql.py::mysqlDBTest::testDateUnset <- test/db_test_base.py SKIPPED [ 55%] test/test_mysql.py::mysqlDBTest::testDefault_Value <- test/db_test_base.py SKIPPED [ 55%] test/test_mysql.py::mysqlDBTest::testDestroyBlob <- test/db_test_base.py SKIPPED [ 55%] test/test_mysql.py::mysqlDBTest::testDestroyJournalling <- test/db_test_base.py SKIPPED [ 55%] test/test_mysql.py::mysqlDBTest::testDestroyNoJournalling <- test/db_test_base.py SKIPPED [ 55%] test/test_mysql.py::mysqlDBTest::testDoubleChange <- test/db_test_base.py SKIPPED [ 55%] test/test_mysql.py::mysqlDBTest::testDoubleUnset <- test/db_test_base.py SKIPPED [ 55%] test/test_mysql.py::mysqlDBTest::testEmptySet <- test/db_test_base.py SKIPPED [ 55%] test/test_mysql.py::mysqlDBTest::testExceptions <- test/db_test_base.py SKIPPED [ 55%] test/test_mysql.py::mysqlDBTest::testFileClassContentChange <- test/db_test_base.py SKIPPED [ 55%] test/test_mysql.py::mysqlDBTest::testFileClassIndexingNoNoNo <- test/db_test_base.py SKIPPED [ 56%] test/test_mysql.py::mysqlDBTest::testFileClassReindexing <- test/db_test_base.py SKIPPED [ 56%] test/test_mysql.py::mysqlDBTest::testFilteringBoolean <- test/db_test_base.py SKIPPED [ 56%] test/test_mysql.py::mysqlDBTest::testFilteringDateRangeMulti <- test/db_test_base.py SKIPPED [ 56%] test/test_mysql.py::mysqlDBTest::testFilteringDateSort <- test/db_test_base.py SKIPPED [ 56%] test/test_mysql.py::mysqlDBTest::testFilteringDateSortPriorityGroup <- test/db_test_base.py SKIPPED [ 56%] test/test_mysql.py::mysqlDBTest::testFilteringID <- test/db_test_base.py SKIPPED [ 56%] test/test_mysql.py::mysqlDBTest::testFilteringIntervalSort <- test/db_test_base.py SKIPPED [ 56%] test/test_mysql.py::mysqlDBTest::testFilteringLink <- test/db_test_base.py SKIPPED [ 56%] test/test_mysql.py::mysqlDBTest::testFilteringLinkSortGroup <- test/db_test_base.py SKIPPED [ 56%] test/test_mysql.py::mysqlDBTest::testFilteringLinkSortSearchMultilink <- test/db_test_base.py SKIPPED [ 56%] test/test_mysql.py::mysqlDBTest::testFilteringMany <- test/db_test_base.py SKIPPED [ 56%] test/test_mysql.py::mysqlDBTest::testFilteringMultilink <- test/db_test_base.py SKIPPED [ 56%] test/test_mysql.py::mysqlDBTest::testFilteringMultilinkAndGroup <- test/db_test_base.py SKIPPED [ 56%] test/test_mysql.py::mysqlDBTest::testFilteringMultilinkSort <- test/db_test_base.py SKIPPED [ 56%] test/test_mysql.py::mysqlDBTest::testFilteringMultilinkSortGroup <- test/db_test_base.py SKIPPED [ 57%] test/test_mysql.py::mysqlDBTest::testFilteringNumber <- test/db_test_base.py SKIPPED [ 57%] test/test_mysql.py::mysqlDBTest::testFilteringRangeBasic <- test/db_test_base.py SKIPPED [ 57%] test/test_mysql.py::mysqlDBTest::testFilteringRangeGeekInterval <- test/db_test_base.py SKIPPED [ 57%] test/test_mysql.py::mysqlDBTest::testFilteringRangeInterval <- test/db_test_base.py SKIPPED [ 57%] test/test_mysql.py::mysqlDBTest::testFilteringRangeMonths <- test/db_test_base.py SKIPPED [ 57%] test/test_mysql.py::mysqlDBTest::testFilteringRangeTwoSyntaxes <- test/db_test_base.py SKIPPED [ 57%] test/test_mysql.py::mysqlDBTest::testFilteringRangeYearMonthDay <- test/db_test_base.py SKIPPED [ 57%] test/test_mysql.py::mysqlDBTest::testFilteringRetired <- test/db_test_base.py SKIPPED [ 57%] test/test_mysql.py::mysqlDBTest::testFilteringRetiredString <- test/db_test_base.py SKIPPED [ 57%] test/test_mysql.py::mysqlDBTest::testFilteringRevLink <- test/db_test_base.py SKIPPED [ 57%] test/test_mysql.py::mysqlDBTest::testFilteringRevMultilink <- test/db_test_base.py SKIPPED [ 57%] test/test_mysql.py::mysqlDBTest::testFilteringSortId <- test/db_test_base.py SKIPPED [ 57%] test/test_mysql.py::mysqlDBTest::testFilteringSpecialChars <- test/db_test_base.py SKIPPED [ 57%] test/test_mysql.py::mysqlDBTest::testFilteringString <- test/db_test_base.py SKIPPED [ 57%] test/test_mysql.py::mysqlDBTest::testFilteringStringCase <- test/db_test_base.py SKIPPED [ 58%] test/test_mysql.py::mysqlDBTest::testFilteringStringExactMatch <- test/db_test_base.py SKIPPED [ 58%] test/test_mysql.py::mysqlDBTest::testFilteringStringSort <- test/db_test_base.py SKIPPED [ 58%] test/test_mysql.py::mysqlDBTest::testFilteringTransitiveLinkIssue <- test/db_test_base.py SKIPPED [ 58%] test/test_mysql.py::mysqlDBTest::testFilteringTransitiveLinkSort <- test/db_test_base.py SKIPPED [ 58%] test/test_mysql.py::mysqlDBTest::testFilteringTransitiveLinkSortNull <- test/db_test_base.py SKIPPED [ 58%] test/test_mysql.py::mysqlDBTest::testFilteringTransitiveLinkUser <- test/db_test_base.py SKIPPED [ 58%] test/test_mysql.py::mysqlDBTest::testFilteringTransitiveLinkUserLimit <- test/db_test_base.py SKIPPED [ 58%] test/test_mysql.py::mysqlDBTest::testFilteringTransitiveMultilink <- test/db_test_base.py SKIPPED [ 58%] test/test_mysql.py::mysqlDBTest::testFilteringTransitiveMultilinkSort <- test/db_test_base.py SKIPPED [ 58%] test/test_mysql.py::mysqlDBTest::testFindIncorrectProperty <- test/db_test_base.py SKIPPED [ 58%] test/test_mysql.py::mysqlDBTest::testFindLink <- test/db_test_base.py SKIPPED [ 58%] test/test_mysql.py::mysqlDBTest::testFindLinkAndMultilink <- test/db_test_base.py SKIPPED [ 58%] test/test_mysql.py::mysqlDBTest::testFindLinkFail <- test/db_test_base.py SKIPPED [ 58%] test/test_mysql.py::mysqlDBTest::testFindLinkUnset <- test/db_test_base.py SKIPPED [ 58%] test/test_mysql.py::mysqlDBTest::testFindMultiMultilink <- test/db_test_base.py SKIPPED [ 59%] test/test_mysql.py::mysqlDBTest::testFindMultilink <- test/db_test_base.py SKIPPED [ 59%] test/test_mysql.py::mysqlDBTest::testFindMultilinkFail <- test/db_test_base.py SKIPPED [ 59%] test/test_mysql.py::mysqlDBTest::testFindMultilinkUnset <- test/db_test_base.py SKIPPED [ 59%] test/test_mysql.py::mysqlDBTest::testFindMultipleLink <- test/db_test_base.py SKIPPED [ 59%] test/test_mysql.py::mysqlDBTest::testFindRetired <- test/db_test_base.py SKIPPED [ 59%] test/test_mysql.py::mysqlDBTest::testFindRevLinkMultilink <- test/db_test_base.py SKIPPED [ 59%] test/test_mysql.py::mysqlDBTest::testForcedReindexing <- test/db_test_base.py SKIPPED [ 59%] test/test_mysql.py::mysqlDBTest::testIDGeneration <- test/db_test_base.py SKIPPED [ 59%] test/test_mysql.py::mysqlDBTest::testIDSetting <- test/db_test_base.py SKIPPED [ 59%] test/test_mysql.py::mysqlDBTest::testImportExport <- test/db_test_base.py SKIPPED [ 59%] test/test_mysql.py::mysqlDBTest::testIndexerSearchMulti <- test/db_test_base.py SKIPPED [ 59%] test/test_mysql.py::mysqlDBTest::testIndexerSearching <- test/db_test_base.py SKIPPED [ 59%] test/test_mysql.py::mysqlDBTest::testIndexerSearchingLink <- test/db_test_base.py SKIPPED [ 59%] test/test_mysql.py::mysqlDBTest::testIndexingPropertiesOnImport <- test/db_test_base.py SKIPPED [ 60%] test/test_mysql.py::mysqlDBTest::testIntegerChange <- test/db_test_base.py SKIPPED [ 60%] test/test_mysql.py::mysqlDBTest::testIntegerUnset <- test/db_test_base.py SKIPPED [ 60%] test/test_mysql.py::mysqlDBTest::testIntervalChange <- test/db_test_base.py SKIPPED [ 60%] test/test_mysql.py::mysqlDBTest::testIntervalUnset <- test/db_test_base.py SKIPPED [ 60%] test/test_mysql.py::mysqlDBTest::testJournalNonexistingProperty <- test/db_test_base.py SKIPPED [ 60%] test/test_mysql.py::mysqlDBTest::testJournalPreCommit <- test/db_test_base.py SKIPPED [ 60%] test/test_mysql.py::mysqlDBTest::testJournals <- test/db_test_base.py SKIPPED [ 60%] test/test_mysql.py::mysqlDBTest::testKeyValue <- test/db_test_base.py SKIPPED [ 60%] test/test_mysql.py::mysqlDBTest::testLabelProp <- test/db_test_base.py SKIPPED [ 60%] test/test_mysql.py::mysqlDBTest::testLinkChange <- test/db_test_base.py SKIPPED [ 60%] test/test_mysql.py::mysqlDBTest::testLinkUnset <- test/db_test_base.py SKIPPED [ 60%] test/test_mysql.py::mysqlDBTest::testMakeSeveralMultilinkedNodes <- test/db_test_base.py SKIPPED [ 60%] test/test_mysql.py::mysqlDBTest::testMultilinkChange <- test/db_test_base.py SKIPPED [ 60%] test/test_mysql.py::mysqlDBTest::testMultilinkChangeIterable <- test/db_test_base.py SKIPPED [ 60%] test/test_mysql.py::mysqlDBTest::testNosyMail <- test/db_test_base.py SKIPPED [ 61%] test/test_mysql.py::mysqlDBTest::testNosyMailTextAndBinary <- test/db_test_base.py SKIPPED [ 61%] test/test_mysql.py::mysqlDBTest::testNumberChange <- test/db_test_base.py SKIPPED [ 61%] test/test_mysql.py::mysqlDBTest::testNumberUnset <- test/db_test_base.py SKIPPED [ 61%] test/test_mysql.py::mysqlDBTest::testPGPNosyMail <- test/db_test_base.py SKIPPED [ 61%] test/test_mysql.py::mysqlDBTest::testPack <- test/db_test_base.py SKIPPED [ 61%] test/test_mysql.py::mysqlDBTest::testPasswordChange <- test/db_test_base.py SKIPPED [ 61%] test/test_mysql.py::mysqlDBTest::testPasswordUnset <- test/db_test_base.py SKIPPED [ 61%] test/test_mysql.py::mysqlDBTest::testQuietChangenote <- test/db_test_base.py SKIPPED [ 61%] test/test_mysql.py::mysqlDBTest::testQuietJournal <- test/db_test_base.py SKIPPED [ 61%] test/test_mysql.py::mysqlDBTest::testQuietProperty <- test/db_test_base.py SKIPPED [ 61%] test/test_mysql.py::mysqlDBTest::testRefresh <- test/db_test_base.py SKIPPED [ 61%] test/test_mysql.py::mysqlDBTest::testReindexingChange <- test/db_test_base.py SKIPPED [ 61%] test/test_mysql.py::mysqlDBTest::testReindexingClear <- test/db_test_base.py SKIPPED [ 61%] test/test_mysql.py::mysqlDBTest::testRemoveProperty <- test/db_test_base.py SKIPPED [ 61%] test/test_mysql.py::mysqlDBTest::testRetire <- test/db_test_base.py SKIPPED [ 62%] test/test_mysql.py::mysqlDBTest::testSerialisation <- test/db_test_base.py SKIPPED [ 62%] test/test_mysql.py::mysqlDBTest::testStringBinary <- test/db_test_base.py SKIPPED [ 62%] test/test_mysql.py::mysqlDBTest::testStringChange <- test/db_test_base.py SKIPPED [ 62%] test/test_mysql.py::mysqlDBTest::testStringFind <- test/db_test_base.py SKIPPED [ 62%] test/test_mysql.py::mysqlDBTest::testStringUnicode <- test/db_test_base.py SKIPPED [ 62%] test/test_mysql.py::mysqlDBTest::testStringUnset <- test/db_test_base.py SKIPPED [ 62%] test/test_mysql.py::mysqlDBTest::testTransactions <- test/db_test_base.py SKIPPED [ 62%] test/test_mysql.py::mysqlDBTest::testViewPremJournal <- test/db_test_base.py SKIPPED [ 62%] test/test_mysql.py::mysqlROTest::testExceptions <- test/db_test_base.py SKIPPED [ 62%] test/test_mysql.py::mysqlSchemaTest::test_addNewClass <- test/db_test_base.py SKIPPED [ 62%] test/test_mysql.py::mysqlSchemaTest::test_changeClassKey <- test/db_test_base.py SKIPPED [ 62%] test/test_mysql.py::mysqlSchemaTest::test_fileClassProps <- test/db_test_base.py SKIPPED [ 62%] test/test_mysql.py::mysqlSchemaTest::test_makeNewMultilink <- test/db_test_base.py SKIPPED [ 62%] test/test_mysql.py::mysqlSchemaTest::test_modifyClass <- test/db_test_base.py SKIPPED [ 63%] test/test_mysql.py::mysqlSchemaTest::test_removeClass <- test/db_test_base.py SKIPPED [ 63%] test/test_mysql.py::mysqlSchemaTest::test_removeClassKey <- test/db_test_base.py SKIPPED [ 63%] test/test_mysql.py::mysqlSchemaTest::test_removeMultilink <- test/db_test_base.py SKIPPED [ 63%] test/test_mysql.py::mysqlSchemaTest::test_reservedProperties <- test/db_test_base.py SKIPPED [ 63%] test/test_mysql.py::mysqlClassicInitTest::testCreation <- test/db_test_base.py SKIPPED [ 63%] test/test_mysql.py::mysqlConcurrencyTest::testConcurrency <- test/db_test_base.py SKIPPED [ 63%] test/test_mysql.py::mysqlHTMLItemTest::testHTMLItemAttributes <- test/db_test_base.py SKIPPED [ 63%] test/test_mysql.py::mysqlHTMLItemTest::testHTMLItemDerefFail <- test/db_test_base.py SKIPPED [ 63%] test/test_mysql.py::mysqlHTMLItemTest::testHTMLItemDereference <- test/db_test_base.py SKIPPED [ 63%] test/test_mysql.py::mysqlFilterCacheTest::testFilteringTransitiveLinkCache <- test/db_test_base.py SKIPPED [ 63%] test/test_mysql.py::mysqlSessionTest::testDestroy <- test/session_common.py SKIPPED [ 63%] test/test_mysql.py::mysqlSessionTest::testGetAll <- test/session_common.py SKIPPED [ 63%] test/test_mysql.py::mysqlSessionTest::testList <- test/session_common.py SKIPPED [ 63%] test/test_mysql.py::mysqlSessionTest::testSetSession <- test/session_common.py SKIPPED [ 63%] test/test_mysql.py::mysqlSessionTest::testUpdateSession <- test/session_common.py SKIPPED [ 64%] test/test_mysql.py::mysqlSpecialActionTestCase::testInnerMain <- test/db_test_base.py SKIPPED [ 64%] test/test_mysql.py::mysqlRestTest::testAcceptHeaderParsing <- test/rest_common.py SKIPPED [ 64%] test/test_mysql.py::mysqlRestTest::testAuthAllowedPost <- test/rest_common.py SKIPPED [ 64%] test/test_mysql.py::mysqlRestTest::testAuthAllowedPut <- test/rest_common.py SKIPPED [ 64%] test/test_mysql.py::mysqlRestTest::testAuthDeniedPost <- test/rest_common.py SKIPPED [ 64%] test/test_mysql.py::mysqlRestTest::testAuthDeniedPut <- test/rest_common.py SKIPPED [ 64%] test/test_mysql.py::mysqlRestTest::testBinaryFieldStorage <- test/rest_common.py SKIPPED [ 64%] test/test_mysql.py::mysqlRestTest::testDeleteAttributeUri <- test/rest_common.py SKIPPED [ 64%] test/test_mysql.py::mysqlRestTest::testDispatch <- test/rest_common.py SKIPPED [ 64%] test/test_mysql.py::mysqlRestTest::testDispatchPost <- test/rest_common.py SKIPPED [ 64%] test/test_mysql.py::mysqlRestTest::testEtagGeneration <- test/rest_common.py SKIPPED [ 64%] test/test_mysql.py::mysqlRestTest::testEtagProcessing <- test/rest_common.py SKIPPED [ 64%] test/test_mysql.py::mysqlRestTest::testFilter <- test/rest_common.py SKIPPED [ 64%] test/test_mysql.py::mysqlRestTest::testGet <- test/rest_common.py SKIPPED [ 64%] test/test_mysql.py::mysqlRestTest::testGetExactMatch <- test/rest_common.py SKIPPED [ 65%] test/test_mysql.py::mysqlRestTest::testGetTransitive <- test/rest_common.py SKIPPED [ 65%] test/test_mysql.py::mysqlRestTest::testMethodOverride <- test/rest_common.py SKIPPED [ 65%] test/test_mysql.py::mysqlRestTest::testOutputFormat <- test/rest_common.py SKIPPED [ 65%] test/test_mysql.py::mysqlRestTest::testPagination <- test/rest_common.py SKIPPED [ 65%] test/test_mysql.py::mysqlRestTest::testPatchAction <- test/rest_common.py SKIPPED [ 65%] test/test_mysql.py::mysqlRestTest::testPatchAdd <- test/rest_common.py SKIPPED [ 65%] test/test_mysql.py::mysqlRestTest::testPatchRemove <- test/rest_common.py SKIPPED [ 65%] test/test_mysql.py::mysqlRestTest::testPatchRemoveAll <- test/rest_common.py SKIPPED [ 65%] test/test_mysql.py::mysqlRestTest::testPatchReplace <- test/rest_common.py SKIPPED [ 65%] test/test_mysql.py::mysqlRestTest::testPost <- test/rest_common.py SKIPPED [ 65%] test/test_mysql.py::mysqlRestTest::testPostFile <- test/rest_common.py SKIPPED [ 65%] test/test_mysql.py::mysqlRestTest::testPostPOE <- test/rest_common.py SKIPPED [ 65%] test/test_mysql.py::mysqlRestTest::testPutAttribute <- test/rest_common.py SKIPPED [ 65%] test/test_mysql.py::mysqlRestTest::testPutElement <- test/rest_common.py SKIPPED [ 65%] test/test_mysql.py::mysqlRestTest::testRestRateLimit <- test/rest_common.py SKIPPED [ 66%] test/test_mysql.py::mysqlRestTest::testSorting <- test/rest_common.py SKIPPED [ 66%] test/test_mysql.py::mysqlRestTest::testStatsGen <- test/rest_common.py SKIPPED [ 66%] test/test_mysql.py::mysqlRestTest::testTransitiveField <- test/rest_common.py SKIPPED [ 66%] test/test_mysql.py::mysqlRestTest::test_bad_audience_jwt <- test/rest_common.py SKIPPED [ 66%] test/test_mysql.py::mysqlRestTest::test_bad_issue_jwt <- test/rest_common.py SKIPPED [ 66%] test/test_mysql.py::mysqlRestTest::test_bad_roles_jwt <- test/rest_common.py SKIPPED [ 66%] test/test_mysql.py::mysqlRestTest::test_bad_subject_jwt <- test/rest_common.py SKIPPED [ 66%] test/test_mysql.py::mysqlRestTest::test_disabled_jwt <- test/rest_common.py SKIPPED [ 66%] test/test_mysql.py::mysqlRestTest::test_expired_jwt <- test/rest_common.py SKIPPED [ 66%] test/test_mysql.py::mysqlRestTest::test_user_email_jwt <- test/rest_common.py SKIPPED [ 66%] test/test_mysql.py::mysqlRestTest::test_user_emailnorest_jwt <- test/rest_common.py SKIPPED [ 66%] test/test_mysql.py::mysqlRestTest::test_user_jwt <- test/rest_common.py SKIPPED [ 66%] test/test_postgresql.py::postgresqlDBTest::testActorProperty <- test/db_test_base.py SKIPPED [ 66%] test/test_postgresql.py::postgresqlDBTest::testAddProperty <- test/db_test_base.py SKIPPED [ 67%] test/test_postgresql.py::postgresqlDBTest::testAddRemoveProperty <- test/db_test_base.py SKIPPED [ 67%] test/test_postgresql.py::postgresqlDBTest::testAdminDuplicateInitialisation <- test/db_test_base.py SKIPPED [ 67%] test/test_postgresql.py::postgresqlDBTest::testAdminImportExport <- test/db_test_base.py SKIPPED [ 67%] test/test_postgresql.py::postgresqlDBTest::testAdminOtherCommands <- test/db_test_base.py SKIPPED [ 67%] test/test_postgresql.py::postgresqlDBTest::testAuditorTwo <- test/db_test_base.py SKIPPED [ 67%] test/test_postgresql.py::postgresqlDBTest::testAuditors <- test/db_test_base.py SKIPPED [ 67%] test/test_postgresql.py::postgresqlDBTest::testBooleanChange <- test/db_test_base.py SKIPPED [ 67%] test/test_postgresql.py::postgresqlDBTest::testBooleanSet <- test/db_test_base.py SKIPPED [ 67%] test/test_postgresql.py::postgresqlDBTest::testBooleanUnset <- test/db_test_base.py SKIPPED [ 67%] test/test_postgresql.py::postgresqlDBTest::testCacheCreateSet <- test/db_test_base.py SKIPPED [ 67%] test/test_postgresql.py::postgresqlDBTest::testCreatorProperty <- test/db_test_base.py SKIPPED [ 67%] test/test_postgresql.py::postgresqlDBTest::testDateChange <- test/db_test_base.py SKIPPED [ 67%] test/test_postgresql.py::postgresqlDBTest::testDateLeapYear <- test/db_test_base.py SKIPPED [ 67%] test/test_postgresql.py::postgresqlDBTest::testDateSort <- test/db_test_base.py SKIPPED [ 67%] test/test_postgresql.py::postgresqlDBTest::testDateSortMultilink <- test/db_test_base.py SKIPPED [ 68%] test/test_postgresql.py::postgresqlDBTest::testDateUnset <- test/db_test_base.py SKIPPED [ 68%] test/test_postgresql.py::postgresqlDBTest::testDefault_Value <- test/db_test_base.py SKIPPED [ 68%] test/test_postgresql.py::postgresqlDBTest::testDestroyBlob <- test/db_test_base.py SKIPPED [ 68%] test/test_postgresql.py::postgresqlDBTest::testDestroyJournalling <- test/db_test_base.py SKIPPED [ 68%] test/test_postgresql.py::postgresqlDBTest::testDestroyNoJournalling <- test/db_test_base.py SKIPPED [ 68%] test/test_postgresql.py::postgresqlDBTest::testDoubleChange <- test/db_test_base.py SKIPPED [ 68%] test/test_postgresql.py::postgresqlDBTest::testDoubleUnset <- test/db_test_base.py SKIPPED [ 68%] test/test_postgresql.py::postgresqlDBTest::testEmptySet <- test/db_test_base.py SKIPPED [ 68%] test/test_postgresql.py::postgresqlDBTest::testExceptions <- test/db_test_base.py SKIPPED [ 68%] test/test_postgresql.py::postgresqlDBTest::testFileClassContentChange <- test/db_test_base.py SKIPPED [ 68%] test/test_postgresql.py::postgresqlDBTest::testFileClassIndexingNoNoNo <- test/db_test_base.py SKIPPED [ 68%] test/test_postgresql.py::postgresqlDBTest::testFileClassReindexing <- test/db_test_base.py SKIPPED [ 68%] test/test_postgresql.py::postgresqlDBTest::testFilteringBoolean <- test/db_test_base.py SKIPPED [ 68%] test/test_postgresql.py::postgresqlDBTest::testFilteringDateRangeMulti <- test/db_test_base.py SKIPPED [ 68%] test/test_postgresql.py::postgresqlDBTest::testFilteringDateSort <- test/db_test_base.py SKIPPED [ 69%] test/test_postgresql.py::postgresqlDBTest::testFilteringDateSortPriorityGroup <- test/db_test_base.py SKIPPED [ 69%] test/test_postgresql.py::postgresqlDBTest::testFilteringID <- test/db_test_base.py SKIPPED [ 69%] test/test_postgresql.py::postgresqlDBTest::testFilteringIntervalSort <- test/db_test_base.py SKIPPED [ 69%] test/test_postgresql.py::postgresqlDBTest::testFilteringLink <- test/db_test_base.py SKIPPED [ 69%] test/test_postgresql.py::postgresqlDBTest::testFilteringLinkSortGroup <- test/db_test_base.py SKIPPED [ 69%] test/test_postgresql.py::postgresqlDBTest::testFilteringLinkSortSearchMultilink <- test/db_test_base.py SKIPPED [ 69%] test/test_postgresql.py::postgresqlDBTest::testFilteringMany <- test/db_test_base.py SKIPPED [ 69%] test/test_postgresql.py::postgresqlDBTest::testFilteringMultilink <- test/db_test_base.py SKIPPED [ 69%] test/test_postgresql.py::postgresqlDBTest::testFilteringMultilinkAndGroup <- test/db_test_base.py SKIPPED [ 69%] test/test_postgresql.py::postgresqlDBTest::testFilteringMultilinkSort <- test/db_test_base.py SKIPPED [ 69%] test/test_postgresql.py::postgresqlDBTest::testFilteringMultilinkSortGroup <- test/db_test_base.py SKIPPED [ 69%] test/test_postgresql.py::postgresqlDBTest::testFilteringNumber <- test/db_test_base.py SKIPPED [ 69%] test/test_postgresql.py::postgresqlDBTest::testFilteringRangeBasic <- test/db_test_base.py SKIPPED [ 69%] test/test_postgresql.py::postgresqlDBTest::testFilteringRangeGeekInterval <- test/db_test_base.py SKIPPED [ 69%] test/test_postgresql.py::postgresqlDBTest::testFilteringRangeInterval <- test/db_test_base.py SKIPPED [ 70%] test/test_postgresql.py::postgresqlDBTest::testFilteringRangeMonths <- test/db_test_base.py SKIPPED [ 70%] test/test_postgresql.py::postgresqlDBTest::testFilteringRangeTwoSyntaxes <- test/db_test_base.py SKIPPED [ 70%] test/test_postgresql.py::postgresqlDBTest::testFilteringRangeYearMonthDay <- test/db_test_base.py SKIPPED [ 70%] test/test_postgresql.py::postgresqlDBTest::testFilteringRetired <- test/db_test_base.py SKIPPED [ 70%] test/test_postgresql.py::postgresqlDBTest::testFilteringRetiredString <- test/db_test_base.py SKIPPED [ 70%] test/test_postgresql.py::postgresqlDBTest::testFilteringRevLink <- test/db_test_base.py SKIPPED [ 70%] test/test_postgresql.py::postgresqlDBTest::testFilteringRevMultilink <- test/db_test_base.py SKIPPED [ 70%] test/test_postgresql.py::postgresqlDBTest::testFilteringSortId <- test/db_test_base.py SKIPPED [ 70%] test/test_postgresql.py::postgresqlDBTest::testFilteringSpecialChars <- test/db_test_base.py SKIPPED [ 70%] test/test_postgresql.py::postgresqlDBTest::testFilteringString <- test/db_test_base.py SKIPPED [ 70%] test/test_postgresql.py::postgresqlDBTest::testFilteringStringCase <- test/db_test_base.py SKIPPED [ 70%] test/test_postgresql.py::postgresqlDBTest::testFilteringStringExactMatch <- test/db_test_base.py SKIPPED [ 70%] test/test_postgresql.py::postgresqlDBTest::testFilteringStringSort <- test/db_test_base.py SKIPPED [ 70%] test/test_postgresql.py::postgresqlDBTest::testFilteringTransitiveLinkIssue <- test/db_test_base.py SKIPPED [ 71%] test/test_postgresql.py::postgresqlDBTest::testFilteringTransitiveLinkSort <- test/db_test_base.py SKIPPED [ 71%] test/test_postgresql.py::postgresqlDBTest::testFilteringTransitiveLinkSortNull <- test/db_test_base.py SKIPPED [ 71%] test/test_postgresql.py::postgresqlDBTest::testFilteringTransitiveLinkUser <- test/db_test_base.py SKIPPED [ 71%] test/test_postgresql.py::postgresqlDBTest::testFilteringTransitiveLinkUserLimit <- test/db_test_base.py SKIPPED [ 71%] test/test_postgresql.py::postgresqlDBTest::testFilteringTransitiveMultilink <- test/db_test_base.py SKIPPED [ 71%] test/test_postgresql.py::postgresqlDBTest::testFilteringTransitiveMultilinkSort <- test/db_test_base.py SKIPPED [ 71%] test/test_postgresql.py::postgresqlDBTest::testFindIncorrectProperty <- test/db_test_base.py SKIPPED [ 71%] test/test_postgresql.py::postgresqlDBTest::testFindLink <- test/db_test_base.py SKIPPED [ 71%] test/test_postgresql.py::postgresqlDBTest::testFindLinkAndMultilink <- test/db_test_base.py SKIPPED [ 71%] test/test_postgresql.py::postgresqlDBTest::testFindLinkFail <- test/db_test_base.py SKIPPED [ 71%] test/test_postgresql.py::postgresqlDBTest::testFindLinkUnset <- test/db_test_base.py SKIPPED [ 71%] test/test_postgresql.py::postgresqlDBTest::testFindMultiMultilink <- test/db_test_base.py SKIPPED [ 71%] test/test_postgresql.py::postgresqlDBTest::testFindMultilink <- test/db_test_base.py SKIPPED [ 71%] test/test_postgresql.py::postgresqlDBTest::testFindMultilinkFail <- test/db_test_base.py SKIPPED [ 71%] test/test_postgresql.py::postgresqlDBTest::testFindMultilinkUnset <- test/db_test_base.py SKIPPED [ 72%] test/test_postgresql.py::postgresqlDBTest::testFindMultipleLink <- test/db_test_base.py SKIPPED [ 72%] test/test_postgresql.py::postgresqlDBTest::testFindRetired <- test/db_test_base.py SKIPPED [ 72%] test/test_postgresql.py::postgresqlDBTest::testFindRevLinkMultilink <- test/db_test_base.py SKIPPED [ 72%] test/test_postgresql.py::postgresqlDBTest::testForcedReindexing <- test/db_test_base.py SKIPPED [ 72%] test/test_postgresql.py::postgresqlDBTest::testIDGeneration <- test/db_test_base.py SKIPPED [ 72%] test/test_postgresql.py::postgresqlDBTest::testIDSetting <- test/db_test_base.py SKIPPED [ 72%] test/test_postgresql.py::postgresqlDBTest::testImportExport <- test/db_test_base.py SKIPPED [ 72%] test/test_postgresql.py::postgresqlDBTest::testIndexerSearchMulti <- test/db_test_base.py SKIPPED [ 72%] test/test_postgresql.py::postgresqlDBTest::testIndexerSearching <- test/db_test_base.py SKIPPED [ 72%] test/test_postgresql.py::postgresqlDBTest::testIndexerSearchingLink <- test/db_test_base.py SKIPPED [ 72%] test/test_postgresql.py::postgresqlDBTest::testIndexingPropertiesOnImport <- test/db_test_base.py SKIPPED [ 72%] test/test_postgresql.py::postgresqlDBTest::testIntegerChange <- test/db_test_base.py SKIPPED [ 72%] test/test_postgresql.py::postgresqlDBTest::testIntegerUnset <- test/db_test_base.py SKIPPED [ 72%] test/test_postgresql.py::postgresqlDBTest::testIntervalChange <- test/db_test_base.py SKIPPED [ 72%] test/test_postgresql.py::postgresqlDBTest::testIntervalUnset <- test/db_test_base.py SKIPPED [ 73%] test/test_postgresql.py::postgresqlDBTest::testJournalNonexistingProperty <- test/db_test_base.py SKIPPED [ 73%] test/test_postgresql.py::postgresqlDBTest::testJournalPreCommit <- test/db_test_base.py SKIPPED [ 73%] test/test_postgresql.py::postgresqlDBTest::testJournals <- test/db_test_base.py SKIPPED [ 73%] test/test_postgresql.py::postgresqlDBTest::testKeyValue <- test/db_test_base.py SKIPPED [ 73%] test/test_postgresql.py::postgresqlDBTest::testLabelProp <- test/db_test_base.py SKIPPED [ 73%] test/test_postgresql.py::postgresqlDBTest::testLinkChange <- test/db_test_base.py SKIPPED [ 73%] test/test_postgresql.py::postgresqlDBTest::testLinkUnset <- test/db_test_base.py SKIPPED [ 73%] test/test_postgresql.py::postgresqlDBTest::testMakeSeveralMultilinkedNodes <- test/db_test_base.py SKIPPED [ 73%] test/test_postgresql.py::postgresqlDBTest::testMultilinkChange <- test/db_test_base.py SKIPPED [ 73%] test/test_postgresql.py::postgresqlDBTest::testMultilinkChangeIterable <- test/db_test_base.py SKIPPED [ 73%] test/test_postgresql.py::postgresqlDBTest::testNosyMail <- test/db_test_base.py SKIPPED [ 73%] test/test_postgresql.py::postgresqlDBTest::testNosyMailTextAndBinary <- test/db_test_base.py SKIPPED [ 73%] test/test_postgresql.py::postgresqlDBTest::testNumberChange <- test/db_test_base.py SKIPPED [ 73%] test/test_postgresql.py::postgresqlDBTest::testNumberUnset <- test/db_test_base.py SKIPPED [ 73%] test/test_postgresql.py::postgresqlDBTest::testPGPNosyMail <- test/db_test_base.py SKIPPED [ 74%] test/test_postgresql.py::postgresqlDBTest::testPack <- test/db_test_base.py SKIPPED [ 74%] test/test_postgresql.py::postgresqlDBTest::testPasswordChange <- test/db_test_base.py SKIPPED [ 74%] test/test_postgresql.py::postgresqlDBTest::testPasswordUnset <- test/db_test_base.py SKIPPED [ 74%] test/test_postgresql.py::postgresqlDBTest::testQuietChangenote <- test/db_test_base.py SKIPPED [ 74%] test/test_postgresql.py::postgresqlDBTest::testQuietJournal <- test/db_test_base.py SKIPPED [ 74%] test/test_postgresql.py::postgresqlDBTest::testQuietProperty <- test/db_test_base.py SKIPPED [ 74%] test/test_postgresql.py::postgresqlDBTest::testRefresh <- test/db_test_base.py SKIPPED [ 74%] test/test_postgresql.py::postgresqlDBTest::testReindexingChange <- test/db_test_base.py SKIPPED [ 74%] test/test_postgresql.py::postgresqlDBTest::testReindexingClear <- test/db_test_base.py SKIPPED [ 74%] test/test_postgresql.py::postgresqlDBTest::testRemoveProperty <- test/db_test_base.py SKIPPED [ 74%] test/test_postgresql.py::postgresqlDBTest::testRetire <- test/db_test_base.py SKIPPED [ 74%] test/test_postgresql.py::postgresqlDBTest::testSerialisation <- test/db_test_base.py SKIPPED [ 74%] test/test_postgresql.py::postgresqlDBTest::testStringBinary <- test/db_test_base.py SKIPPED [ 74%] test/test_postgresql.py::postgresqlDBTest::testStringChange <- test/db_test_base.py SKIPPED [ 75%] test/test_postgresql.py::postgresqlDBTest::testStringFind <- test/db_test_base.py SKIPPED [ 75%] test/test_postgresql.py::postgresqlDBTest::testStringUnicode <- test/db_test_base.py SKIPPED [ 75%] test/test_postgresql.py::postgresqlDBTest::testStringUnset <- test/db_test_base.py SKIPPED [ 75%] test/test_postgresql.py::postgresqlDBTest::testTransactions <- test/db_test_base.py SKIPPED [ 75%] test/test_postgresql.py::postgresqlDBTest::testViewPremJournal <- test/db_test_base.py SKIPPED [ 75%] test/test_postgresql.py::postgresqlROTest::testExceptions <- test/db_test_base.py SKIPPED [ 75%] test/test_postgresql.py::postgresqlConcurrencyTest::testConcurrency <- test/db_test_base.py SKIPPED [ 75%] test/test_postgresql.py::postgresqlJournalTest::testConcurrentReadCommitted SKIPPED [ 75%] test/test_postgresql.py::postgresqlJournalTest::testConcurrentRepeatableRead SKIPPED [ 75%] test/test_postgresql.py::postgresqlHTMLItemTest::testHTMLItemAttributes <- test/db_test_base.py SKIPPED [ 75%] test/test_postgresql.py::postgresqlHTMLItemTest::testHTMLItemDerefFail <- test/db_test_base.py SKIPPED [ 75%] test/test_postgresql.py::postgresqlHTMLItemTest::testHTMLItemDereference <- test/db_test_base.py SKIPPED [ 75%] test/test_postgresql.py::postgresqlFilterCacheTest::testFilteringTransitiveLinkCache <- test/db_test_base.py SKIPPED [ 75%] test/test_postgresql.py::postgresqlSchemaTest::test_addNewClass <- test/db_test_base.py SKIPPED [ 75%] test/test_postgresql.py::postgresqlSchemaTest::test_changeClassKey <- test/db_test_base.py SKIPPED [ 76%] test/test_postgresql.py::postgresqlSchemaTest::test_fileClassProps <- test/db_test_base.py SKIPPED [ 76%] test/test_postgresql.py::postgresqlSchemaTest::test_makeNewMultilink <- test/db_test_base.py SKIPPED [ 76%] test/test_postgresql.py::postgresqlSchemaTest::test_modifyClass <- test/db_test_base.py SKIPPED [ 76%] test/test_postgresql.py::postgresqlSchemaTest::test_removeClass <- test/db_test_base.py SKIPPED [ 76%] test/test_postgresql.py::postgresqlSchemaTest::test_removeClassKey <- test/db_test_base.py SKIPPED [ 76%] test/test_postgresql.py::postgresqlSchemaTest::test_removeMultilink <- test/db_test_base.py SKIPPED [ 76%] test/test_postgresql.py::postgresqlSchemaTest::test_reservedProperties <- test/db_test_base.py SKIPPED [ 76%] test/test_postgresql.py::postgresqlClassicInitTest::testCreation <- test/db_test_base.py SKIPPED [ 76%] test/test_postgresql.py::postgresqlSessionTest::testDestroy <- test/session_common.py SKIPPED [ 76%] test/test_postgresql.py::postgresqlSessionTest::testGetAll <- test/session_common.py SKIPPED [ 76%] test/test_postgresql.py::postgresqlSessionTest::testList <- test/session_common.py SKIPPED [ 76%] test/test_postgresql.py::postgresqlSessionTest::testSetSession <- test/session_common.py SKIPPED [ 76%] test/test_postgresql.py::postgresqlSessionTest::testUpdateSession <- test/session_common.py SKIPPED [ 76%] test/test_postgresql.py::postgresqlSpecialActionTestCase::testInnerMain <- test/db_test_base.py SKIPPED [ 76%] test/test_postgresql.py::postgresqlRestTest::testAcceptHeaderParsing <- test/rest_common.py SKIPPED [ 77%] test/test_postgresql.py::postgresqlRestTest::testAuthAllowedPost <- test/rest_common.py SKIPPED [ 77%] test/test_postgresql.py::postgresqlRestTest::testAuthAllowedPut <- test/rest_common.py SKIPPED [ 77%] test/test_postgresql.py::postgresqlRestTest::testAuthDeniedPost <- test/rest_common.py SKIPPED [ 77%] test/test_postgresql.py::postgresqlRestTest::testAuthDeniedPut <- test/rest_common.py SKIPPED [ 77%] test/test_postgresql.py::postgresqlRestTest::testBinaryFieldStorage <- test/rest_common.py SKIPPED [ 77%] test/test_postgresql.py::postgresqlRestTest::testDeleteAttributeUri <- test/rest_common.py SKIPPED [ 77%] test/test_postgresql.py::postgresqlRestTest::testDispatch <- test/rest_common.py SKIPPED [ 77%] test/test_postgresql.py::postgresqlRestTest::testDispatchPost <- test/rest_common.py SKIPPED [ 77%] test/test_postgresql.py::postgresqlRestTest::testEtagGeneration <- test/rest_common.py SKIPPED [ 77%] test/test_postgresql.py::postgresqlRestTest::testEtagProcessing <- test/rest_common.py SKIPPED [ 77%] test/test_postgresql.py::postgresqlRestTest::testFilter <- test/rest_common.py SKIPPED [ 77%] test/test_postgresql.py::postgresqlRestTest::testGet <- test/rest_common.py SKIPPED [ 77%] test/test_postgresql.py::postgresqlRestTest::testGetExactMatch <- test/rest_common.py SKIPPED [ 77%] test/test_postgresql.py::postgresqlRestTest::testGetTransitive <- test/rest_common.py SKIPPED [ 78%] test/test_postgresql.py::postgresqlRestTest::testMethodOverride <- test/rest_common.py SKIPPED [ 78%] test/test_postgresql.py::postgresqlRestTest::testOutputFormat <- test/rest_common.py SKIPPED [ 78%] test/test_postgresql.py::postgresqlRestTest::testPagination <- test/rest_common.py SKIPPED [ 78%] test/test_postgresql.py::postgresqlRestTest::testPatchAction <- test/rest_common.py SKIPPED [ 78%] test/test_postgresql.py::postgresqlRestTest::testPatchAdd <- test/rest_common.py SKIPPED [ 78%] test/test_postgresql.py::postgresqlRestTest::testPatchRemove <- test/rest_common.py SKIPPED [ 78%] test/test_postgresql.py::postgresqlRestTest::testPatchRemoveAll <- test/rest_common.py SKIPPED [ 78%] test/test_postgresql.py::postgresqlRestTest::testPatchReplace <- test/rest_common.py SKIPPED [ 78%] test/test_postgresql.py::postgresqlRestTest::testPost <- test/rest_common.py SKIPPED [ 78%] test/test_postgresql.py::postgresqlRestTest::testPostFile <- test/rest_common.py SKIPPED [ 78%] test/test_postgresql.py::postgresqlRestTest::testPostPOE <- test/rest_common.py SKIPPED [ 78%] test/test_postgresql.py::postgresqlRestTest::testPutAttribute <- test/rest_common.py SKIPPED [ 78%] test/test_postgresql.py::postgresqlRestTest::testPutElement <- test/rest_common.py SKIPPED [ 78%] test/test_postgresql.py::postgresqlRestTest::testRestRateLimit <- test/rest_common.py SKIPPED [ 78%] test/test_postgresql.py::postgresqlRestTest::testSorting <- test/rest_common.py SKIPPED [ 79%] test/test_postgresql.py::postgresqlRestTest::testStatsGen <- test/rest_common.py SKIPPED [ 79%] test/test_postgresql.py::postgresqlRestTest::testTransitiveField <- test/rest_common.py SKIPPED [ 79%] test/test_postgresql.py::postgresqlRestTest::test_bad_audience_jwt <- test/rest_common.py SKIPPED [ 79%] test/test_postgresql.py::postgresqlRestTest::test_bad_issue_jwt <- test/rest_common.py SKIPPED [ 79%] test/test_postgresql.py::postgresqlRestTest::test_bad_roles_jwt <- test/rest_common.py SKIPPED [ 79%] test/test_postgresql.py::postgresqlRestTest::test_bad_subject_jwt <- test/rest_common.py SKIPPED [ 79%] test/test_postgresql.py::postgresqlRestTest::test_disabled_jwt <- test/rest_common.py SKIPPED [ 79%] test/test_postgresql.py::postgresqlRestTest::test_expired_jwt <- test/rest_common.py SKIPPED [ 79%] test/test_postgresql.py::postgresqlRestTest::test_user_email_jwt <- test/rest_common.py SKIPPED [ 79%] test/test_postgresql.py::postgresqlRestTest::test_user_emailnorest_jwt <- test/rest_common.py SKIPPED [ 79%] test/test_postgresql.py::postgresqlRestTest::test_user_jwt <- test/rest_common.py SKIPPED [ 79%] test/test_pythonexpr.py::ExprTest::testExpr PASSED [ 79%] test/test_schema.py::SchemaTestCase::testA_Status PASSED [ 79%] test/test_schema.py::SchemaTestCase::testB_Issue PASSED [ 79%] test/test_schema.py::SchemaTestCase::testC_User PASSED [ 80%] test/test_security.py::PermissionTest::testAccessControls PASSED [ 80%] test/test_security.py::PermissionTest::testAdmin PASSED [ 80%] test/test_security.py::PermissionTest::testDBinit PASSED [ 80%] test/test_security.py::PermissionTest::testGetPermission PASSED [ 80%] test/test_security.py::PermissionTest::testInitialiseSecurity PASSED [ 80%] test/test_security.py::PermissionTest::testInterfaceSecurity PASSED [ 80%] test/test_security.py::PermissionTest::testTransitiveSearchPermissions PASSED [ 80%] test/test_security.py::PermissionTest::test_password PASSED [ 80%] test/test_sqlite.py::sqliteDBTest::testActorProperty <- test/db_test_base.py PASSED [ 80%] test/test_sqlite.py::sqliteDBTest::testAddProperty <- test/db_test_base.py PASSED [ 80%] test/test_sqlite.py::sqliteDBTest::testAddRemoveProperty <- test/db_test_base.py PASSED [ 80%] test/test_sqlite.py::sqliteDBTest::testAdminDuplicateInitialisation <- test/db_test_base.py PASSED [ 80%] test/test_sqlite.py::sqliteDBTest::testAdminImportExport <- test/db_test_base.py PASSED [ 80%] test/test_sqlite.py::sqliteDBTest::testAdminOtherCommands <- test/db_test_base.py PASSED [ 80%] test/test_sqlite.py::sqliteDBTest::testAuditorTwo <- test/db_test_base.py PASSED [ 81%] test/test_sqlite.py::sqliteDBTest::testAuditors <- test/db_test_base.py PASSED [ 81%] test/test_sqlite.py::sqliteDBTest::testBooleanChange <- test/db_test_base.py PASSED [ 81%] test/test_sqlite.py::sqliteDBTest::testBooleanSet <- test/db_test_base.py PASSED [ 81%] test/test_sqlite.py::sqliteDBTest::testBooleanUnset <- test/db_test_base.py PASSED [ 81%] test/test_sqlite.py::sqliteDBTest::testCacheCreateSet <- test/db_test_base.py PASSED [ 81%] test/test_sqlite.py::sqliteDBTest::testCreatorProperty <- test/db_test_base.py PASSED [ 81%] test/test_sqlite.py::sqliteDBTest::testDateChange <- test/db_test_base.py PASSED [ 81%] test/test_sqlite.py::sqliteDBTest::testDateLeapYear <- test/db_test_base.py PASSED [ 81%] test/test_sqlite.py::sqliteDBTest::testDateSort <- test/db_test_base.py PASSED [ 81%] test/test_sqlite.py::sqliteDBTest::testDateSortMultilink <- test/db_test_base.py PASSED [ 81%] test/test_sqlite.py::sqliteDBTest::testDateUnset <- test/db_test_base.py PASSED [ 81%] test/test_sqlite.py::sqliteDBTest::testDefault_Value <- test/db_test_base.py PASSED [ 81%] test/test_sqlite.py::sqliteDBTest::testDestroyBlob <- test/db_test_base.py PASSED [ 81%] test/test_sqlite.py::sqliteDBTest::testDestroyJournalling <- test/db_test_base.py PASSED [ 82%] test/test_sqlite.py::sqliteDBTest::testDestroyNoJournalling <- test/db_test_base.py PASSED [ 82%] test/test_sqlite.py::sqliteDBTest::testDoubleChange <- test/db_test_base.py PASSED [ 82%] test/test_sqlite.py::sqliteDBTest::testDoubleUnset <- test/db_test_base.py PASSED [ 82%] test/test_sqlite.py::sqliteDBTest::testEmptySet <- test/db_test_base.py PASSED [ 82%] test/test_sqlite.py::sqliteDBTest::testExceptions <- test/db_test_base.py PASSED [ 82%] test/test_sqlite.py::sqliteDBTest::testFileClassContentChange <- test/db_test_base.py PASSED [ 82%] test/test_sqlite.py::sqliteDBTest::testFileClassIndexingNoNoNo <- test/db_test_base.py PASSED [ 82%] test/test_sqlite.py::sqliteDBTest::testFileClassReindexing <- test/db_test_base.py PASSED [ 82%] test/test_sqlite.py::sqliteDBTest::testFilteringBoolean <- test/db_test_base.py PASSED [ 82%] test/test_sqlite.py::sqliteDBTest::testFilteringDateRangeMulti <- test/db_test_base.py PASSED [ 82%] test/test_sqlite.py::sqliteDBTest::testFilteringDateSort <- test/db_test_base.py PASSED [ 82%] test/test_sqlite.py::sqliteDBTest::testFilteringDateSortPriorityGroup <- test/db_test_base.py PASSED [ 82%] test/test_sqlite.py::sqliteDBTest::testFilteringID <- test/db_test_base.py PASSED [ 82%] test/test_sqlite.py::sqliteDBTest::testFilteringIntervalSort <- test/db_test_base.py PASSED [ 82%] test/test_sqlite.py::sqliteDBTest::testFilteringLink <- test/db_test_base.py PASSED [ 83%] test/test_sqlite.py::sqliteDBTest::testFilteringLinkSortGroup <- test/db_test_base.py PASSED [ 83%] test/test_sqlite.py::sqliteDBTest::testFilteringLinkSortSearchMultilink <- test/db_test_base.py PASSED [ 83%] test/test_sqlite.py::sqliteDBTest::testFilteringMany <- test/db_test_base.py PASSED [ 83%] test/test_sqlite.py::sqliteDBTest::testFilteringMultilink <- test/db_test_base.py PASSED [ 83%] test/test_sqlite.py::sqliteDBTest::testFilteringMultilinkAndGroup <- test/db_test_base.py PASSED [ 83%] test/test_sqlite.py::sqliteDBTest::testFilteringMultilinkSort <- test/db_test_base.py PASSED [ 83%] test/test_sqlite.py::sqliteDBTest::testFilteringMultilinkSortGroup <- test/db_test_base.py PASSED [ 83%] test/test_sqlite.py::sqliteDBTest::testFilteringNumber <- test/db_test_base.py PASSED [ 83%] test/test_sqlite.py::sqliteDBTest::testFilteringRangeBasic <- test/db_test_base.py PASSED [ 83%] test/test_sqlite.py::sqliteDBTest::testFilteringRangeGeekInterval <- test/db_test_base.py PASSED [ 83%] test/test_sqlite.py::sqliteDBTest::testFilteringRangeInterval <- test/db_test_base.py PASSED [ 83%] test/test_sqlite.py::sqliteDBTest::testFilteringRangeMonths <- test/db_test_base.py PASSED [ 83%] test/test_sqlite.py::sqliteDBTest::testFilteringRangeTwoSyntaxes <- test/db_test_base.py PASSED [ 83%] test/test_sqlite.py::sqliteDBTest::testFilteringRangeYearMonthDay <- test/db_test_base.py PASSED [ 83%] test/test_sqlite.py::sqliteDBTest::testFilteringRetired <- test/db_test_base.py PASSED [ 84%] test/test_sqlite.py::sqliteDBTest::testFilteringRetiredString <- test/db_test_base.py PASSED [ 84%] test/test_sqlite.py::sqliteDBTest::testFilteringRevLink <- test/db_test_base.py PASSED [ 84%] test/test_sqlite.py::sqliteDBTest::testFilteringRevMultilink <- test/db_test_base.py PASSED [ 84%] test/test_sqlite.py::sqliteDBTest::testFilteringSortId <- test/db_test_base.py PASSED [ 84%] test/test_sqlite.py::sqliteDBTest::testFilteringSpecialChars <- test/db_test_base.py PASSED [ 84%] test/test_sqlite.py::sqliteDBTest::testFilteringString <- test/db_test_base.py PASSED [ 84%] test/test_sqlite.py::sqliteDBTest::testFilteringStringCase <- test/db_test_base.py PASSED [ 84%] test/test_sqlite.py::sqliteDBTest::testFilteringStringExactMatch <- test/db_test_base.py PASSED [ 84%] test/test_sqlite.py::sqliteDBTest::testFilteringStringSort <- test/db_test_base.py PASSED [ 84%] test/test_sqlite.py::sqliteDBTest::testFilteringTransitiveLinkIssue <- test/db_test_base.py PASSED [ 84%] test/test_sqlite.py::sqliteDBTest::testFilteringTransitiveLinkSort <- test/db_test_base.py PASSED [ 84%] test/test_sqlite.py::sqliteDBTest::testFilteringTransitiveLinkSortNull <- test/db_test_base.py PASSED [ 84%] test/test_sqlite.py::sqliteDBTest::testFilteringTransitiveLinkUser <- test/db_test_base.py PASSED [ 84%] test/test_sqlite.py::sqliteDBTest::testFilteringTransitiveLinkUserLimit <- test/db_test_base.py PASSED [ 84%] test/test_sqlite.py::sqliteDBTest::testFilteringTransitiveMultilink <- test/db_test_base.py PASSED [ 85%] test/test_sqlite.py::sqliteDBTest::testFilteringTransitiveMultilinkSort <- test/db_test_base.py PASSED [ 85%] test/test_sqlite.py::sqliteDBTest::testFindIncorrectProperty <- test/db_test_base.py PASSED [ 85%] test/test_sqlite.py::sqliteDBTest::testFindLink <- test/db_test_base.py PASSED [ 85%] test/test_sqlite.py::sqliteDBTest::testFindLinkAndMultilink <- test/db_test_base.py PASSED [ 85%] test/test_sqlite.py::sqliteDBTest::testFindLinkFail <- test/db_test_base.py PASSED [ 85%] test/test_sqlite.py::sqliteDBTest::testFindLinkUnset <- test/db_test_base.py PASSED [ 85%] test/test_sqlite.py::sqliteDBTest::testFindMultiMultilink <- test/db_test_base.py PASSED [ 85%] test/test_sqlite.py::sqliteDBTest::testFindMultilink <- test/db_test_base.py PASSED [ 85%] test/test_sqlite.py::sqliteDBTest::testFindMultilinkFail <- test/db_test_base.py PASSED [ 85%] test/test_sqlite.py::sqliteDBTest::testFindMultilinkUnset <- test/db_test_base.py PASSED [ 85%] test/test_sqlite.py::sqliteDBTest::testFindMultipleLink <- test/db_test_base.py PASSED [ 85%] test/test_sqlite.py::sqliteDBTest::testFindRetired <- test/db_test_base.py PASSED [ 85%] test/test_sqlite.py::sqliteDBTest::testFindRevLinkMultilink <- test/db_test_base.py PASSED [ 85%] test/test_sqlite.py::sqliteDBTest::testForcedReindexing <- test/db_test_base.py PASSED [ 86%] test/test_sqlite.py::sqliteDBTest::testIDGeneration <- test/db_test_base.py PASSED [ 86%] test/test_sqlite.py::sqliteDBTest::testIDSetting <- test/db_test_base.py PASSED [ 86%] test/test_sqlite.py::sqliteDBTest::testImportExport <- test/db_test_base.py PASSED [ 86%] test/test_sqlite.py::sqliteDBTest::testIndexerSearchMulti <- test/db_test_base.py PASSED [ 86%] test/test_sqlite.py::sqliteDBTest::testIndexerSearching <- test/db_test_base.py PASSED [ 86%] test/test_sqlite.py::sqliteDBTest::testIndexerSearchingLink <- test/db_test_base.py PASSED [ 86%] test/test_sqlite.py::sqliteDBTest::testIndexingPropertiesOnImport <- test/db_test_base.py PASSED [ 86%] test/test_sqlite.py::sqliteDBTest::testIntegerChange <- test/db_test_base.py PASSED [ 86%] test/test_sqlite.py::sqliteDBTest::testIntegerUnset <- test/db_test_base.py PASSED [ 86%] test/test_sqlite.py::sqliteDBTest::testIntervalChange <- test/db_test_base.py PASSED [ 86%] test/test_sqlite.py::sqliteDBTest::testIntervalUnset <- test/db_test_base.py PASSED [ 86%] test/test_sqlite.py::sqliteDBTest::testJournalNonexistingProperty <- test/db_test_base.py PASSED [ 86%] test/test_sqlite.py::sqliteDBTest::testJournalPreCommit <- test/db_test_base.py PASSED [ 86%] test/test_sqlite.py::sqliteDBTest::testJournals <- test/db_test_base.py PASSED [ 86%] test/test_sqlite.py::sqliteDBTest::testKeyValue <- test/db_test_base.py PASSED [ 87%] test/test_sqlite.py::sqliteDBTest::testLabelProp <- test/db_test_base.py PASSED [ 87%] test/test_sqlite.py::sqliteDBTest::testLinkChange <- test/db_test_base.py PASSED [ 87%] test/test_sqlite.py::sqliteDBTest::testLinkUnset <- test/db_test_base.py PASSED [ 87%] test/test_sqlite.py::sqliteDBTest::testMakeSeveralMultilinkedNodes <- test/db_test_base.py PASSED [ 87%] test/test_sqlite.py::sqliteDBTest::testMultilinkChange <- test/db_test_base.py PASSED [ 87%] test/test_sqlite.py::sqliteDBTest::testMultilinkChangeIterable <- test/db_test_base.py PASSED [ 87%] test/test_sqlite.py::sqliteDBTest::testNosyMail <- test/db_test_base.py PASSED [ 87%] test/test_sqlite.py::sqliteDBTest::testNosyMailTextAndBinary <- test/db_test_base.py PASSED [ 87%] test/test_sqlite.py::sqliteDBTest::testNumberChange <- test/db_test_base.py PASSED [ 87%] test/test_sqlite.py::sqliteDBTest::testNumberUnset <- test/db_test_base.py PASSED [ 87%] test/test_sqlite.py::sqliteDBTest::testPGPNosyMail <- test/db_test_base.py SKIPPED [ 87%] test/test_sqlite.py::sqliteDBTest::testPack <- test/db_test_base.py PASSED [ 87%] test/test_sqlite.py::sqliteDBTest::testPasswordChange <- test/db_test_base.py PASSED [ 87%] test/test_sqlite.py::sqliteDBTest::testPasswordUnset <- test/db_test_base.py PASSED [ 87%] test/test_sqlite.py::sqliteDBTest::testQuietChangenote <- test/db_test_base.py PASSED [ 88%] test/test_sqlite.py::sqliteDBTest::testQuietJournal <- test/db_test_base.py PASSED [ 88%] test/test_sqlite.py::sqliteDBTest::testQuietProperty <- test/db_test_base.py PASSED [ 88%] test/test_sqlite.py::sqliteDBTest::testRefresh <- test/db_test_base.py PASSED [ 88%] test/test_sqlite.py::sqliteDBTest::testReindexingChange <- test/db_test_base.py PASSED [ 88%] test/test_sqlite.py::sqliteDBTest::testReindexingClear <- test/db_test_base.py PASSED [ 88%] test/test_sqlite.py::sqliteDBTest::testRemoveProperty <- test/db_test_base.py PASSED [ 88%] test/test_sqlite.py::sqliteDBTest::testRetire <- test/db_test_base.py PASSED [ 88%] test/test_sqlite.py::sqliteDBTest::testSerialisation <- test/db_test_base.py PASSED [ 88%] test/test_sqlite.py::sqliteDBTest::testStringBinary <- test/db_test_base.py PASSED [ 88%] test/test_sqlite.py::sqliteDBTest::testStringChange <- test/db_test_base.py PASSED [ 88%] test/test_sqlite.py::sqliteDBTest::testStringFind <- test/db_test_base.py PASSED [ 88%] test/test_sqlite.py::sqliteDBTest::testStringUnicode <- test/db_test_base.py PASSED [ 88%] test/test_sqlite.py::sqliteDBTest::testStringUnset <- test/db_test_base.py PASSED [ 88%] test/test_sqlite.py::sqliteDBTest::testTransactions <- test/db_test_base.py PASSED [ 89%] test/test_sqlite.py::sqliteDBTest::testViewPremJournal <- test/db_test_base.py PASSED [ 89%] test/test_sqlite.py::sqliteROTest::testExceptions <- test/db_test_base.py PASSED [ 89%] test/test_sqlite.py::sqliteSchemaTest::test_addNewClass <- test/db_test_base.py PASSED [ 89%] test/test_sqlite.py::sqliteSchemaTest::test_changeClassKey <- test/db_test_base.py PASSED [ 89%] test/test_sqlite.py::sqliteSchemaTest::test_fileClassProps <- test/db_test_base.py PASSED [ 89%] test/test_sqlite.py::sqliteSchemaTest::test_makeNewMultilink <- test/db_test_base.py PASSED [ 89%] test/test_sqlite.py::sqliteSchemaTest::test_modifyClass <- test/db_test_base.py PASSED [ 89%] test/test_sqlite.py::sqliteSchemaTest::test_removeClass <- test/db_test_base.py PASSED [ 89%] test/test_sqlite.py::sqliteSchemaTest::test_removeClassKey <- test/db_test_base.py PASSED [ 89%] test/test_sqlite.py::sqliteSchemaTest::test_removeMultilink <- test/db_test_base.py PASSED [ 89%] test/test_sqlite.py::sqliteSchemaTest::test_reservedProperties <- test/db_test_base.py PASSED [ 89%] test/test_sqlite.py::sqliteClassicInitTest::testCreation <- test/db_test_base.py PASSED [ 89%] test/test_sqlite.py::sqliteConcurrencyTest::testConcurrency <- test/db_test_base.py PASSED [ 89%] test/test_sqlite.py::sqliteFilterCacheTest::testFilteringTransitiveLinkCache <- test/db_test_base.py PASSED [ 89%] test/test_sqlite.py::sqliteSpecialActionTestCase::testInnerMain <- test/db_test_base.py PASSED [ 90%] test/test_sqlite.py::sqliteSessionTest::testDestroy <- test/session_common.py PASSED [ 90%] test/test_sqlite.py::sqliteSessionTest::testGetAll <- test/session_common.py PASSED [ 90%] test/test_sqlite.py::sqliteSessionTest::testList <- test/session_common.py PASSED [ 90%] test/test_sqlite.py::sqliteSessionTest::testSetSession <- test/session_common.py PASSED [ 90%] test/test_sqlite.py::sqliteSessionTest::testUpdateSession <- test/session_common.py PASSED [ 90%] test/test_sqlite.py::sqliteRestTest::testAcceptHeaderParsing <- test/rest_common.py FAILED [ 90%] test/test_sqlite.py::sqliteRestTest::testAuthAllowedPost <- test/rest_common.py FAILED [ 90%] test/test_sqlite.py::sqliteRestTest::testAuthAllowedPut <- test/rest_common.py FAILED [ 90%] test/test_sqlite.py::sqliteRestTest::testAuthDeniedPost <- test/rest_common.py FAILED [ 90%] test/test_sqlite.py::sqliteRestTest::testAuthDeniedPut <- test/rest_common.py FAILED [ 90%] test/test_sqlite.py::sqliteRestTest::testBinaryFieldStorage <- test/rest_common.py FAILED [ 90%] test/test_sqlite.py::sqliteRestTest::testDeleteAttributeUri <- test/rest_common.py FAILED [ 90%] test/test_sqlite.py::sqliteRestTest::testDispatch <- test/rest_common.py FAILED [ 90%] test/test_sqlite.py::sqliteRestTest::testDispatchPost <- test/rest_common.py FAILED [ 90%] test/test_sqlite.py::sqliteRestTest::testEtagGeneration <- test/rest_common.py FAILED [ 91%] test/test_sqlite.py::sqliteRestTest::testEtagProcessing <- test/rest_common.py FAILED [ 91%] test/test_sqlite.py::sqliteRestTest::testFilter <- test/rest_common.py FAILED [ 91%] test/test_sqlite.py::sqliteRestTest::testGet <- test/rest_common.py FAILED [ 91%] test/test_sqlite.py::sqliteRestTest::testGetExactMatch <- test/rest_common.py FAILED [ 91%] test/test_sqlite.py::sqliteRestTest::testGetTransitive <- test/rest_common.py FAILED [ 91%] test/test_sqlite.py::sqliteRestTest::testMethodOverride <- test/rest_common.py FAILED [ 91%] test/test_sqlite.py::sqliteRestTest::testOutputFormat <- test/rest_common.py FAILED [ 91%] test/test_sqlite.py::sqliteRestTest::testPagination <- test/rest_common.py FAILED [ 91%] test/test_sqlite.py::sqliteRestTest::testPatchAction <- test/rest_common.py FAILED [ 91%] test/test_sqlite.py::sqliteRestTest::testPatchAdd <- test/rest_common.py FAILED [ 91%] test/test_sqlite.py::sqliteRestTest::testPatchRemove <- test/rest_common.py FAILED [ 91%] test/test_sqlite.py::sqliteRestTest::testPatchRemoveAll <- test/rest_common.py FAILED [ 91%] test/test_sqlite.py::sqliteRestTest::testPatchReplace <- test/rest_common.py FAILED [ 91%] test/test_sqlite.py::sqliteRestTest::testPost <- test/rest_common.py FAILED [ 91%] test/test_sqlite.py::sqliteRestTest::testPostFile <- test/rest_common.py FAILED [ 92%] test/test_sqlite.py::sqliteRestTest::testPostPOE <- test/rest_common.py FAILED [ 92%] test/test_sqlite.py::sqliteRestTest::testPutAttribute <- test/rest_common.py FAILED [ 92%] test/test_sqlite.py::sqliteRestTest::testPutElement <- test/rest_common.py FAILED [ 92%] test/test_sqlite.py::sqliteRestTest::testRestRateLimit <- test/rest_common.py FAILED [ 92%] test/test_sqlite.py::sqliteRestTest::testSorting <- test/rest_common.py FAILED [ 92%] test/test_sqlite.py::sqliteRestTest::testStatsGen <- test/rest_common.py FAILED [ 92%] test/test_sqlite.py::sqliteRestTest::testTransitiveField <- test/rest_common.py FAILED [ 92%] test/test_sqlite.py::sqliteRestTest::test_bad_audience_jwt <- test/rest_common.py FAILED [ 92%] test/test_sqlite.py::sqliteRestTest::test_bad_issue_jwt <- test/rest_common.py FAILED [ 92%] test/test_sqlite.py::sqliteRestTest::test_bad_roles_jwt <- test/rest_common.py FAILED [ 92%] test/test_sqlite.py::sqliteRestTest::test_bad_subject_jwt <- test/rest_common.py FAILED [ 92%] test/test_sqlite.py::sqliteRestTest::test_disabled_jwt <- test/rest_common.py FAILED [ 92%] test/test_sqlite.py::sqliteRestTest::test_expired_jwt <- test/rest_common.py FAILED [ 92%] test/test_sqlite.py::sqliteRestTest::test_user_email_jwt <- test/rest_common.py FAILED [ 93%] test/test_sqlite.py::sqliteRestTest::test_user_emailnorest_jwt <- test/rest_common.py FAILED [ 93%] test/test_sqlite.py::sqliteRestTest::test_user_jwt <- test/rest_common.py FAILED [ 93%] test/test_templating.py::HTMLDatabaseTestCase::test_HTMLDatabase___getattr__ PASSED [ 93%] test/test_templating.py::HTMLDatabaseTestCase::test_HTMLDatabase___getitem__ PASSED [ 93%] test/test_templating.py::HTMLDatabaseTestCase::test_HTMLDatabase_classes PASSED [ 93%] test/test_templating.py::FunctionsTestCase::test_lookupIds PASSED [ 93%] test/test_templating.py::FunctionsTestCase::test_lookupKeys PASSED [ 93%] test/test_templating.py::HTMLClassTestCase::test_anti_csrf_nonce PASSED [ 93%] test/test_templating.py::HTMLClassTestCase::test_input_html4 PASSED [ 93%] test/test_templating.py::HTMLClassTestCase::test_input_xhtml PASSED [ 93%] test/test_templating.py::HTMLClassTestCase::test_link PASSED [ 93%] test/test_templating.py::HTMLClassTestCase::test_multilink PASSED [ 93%] test/test_templating.py::HTMLClassTestCase::test_string_email PASSED [ 93%] test/test_templating.py::HTMLClassTestCase::test_string_field PASSED [ 93%] test/test_templating.py::HTMLClassTestCase::test_string_multiline PASSED [ 94%] test/test_templating.py::HTMLClassTestCase::test_string_plain_or_hyperlinked PASSED [ 94%] test/test_templating.py::HTMLClassTestCase::test_string_rst PASSED [ 94%] test/test_templating.py::HTMLClassTestCase::test_string_stext SKIPPED [ 94%] test/test_templating.py::HTMLClassTestCase::test_string_url_quote PASSED [ 94%] test/test_templating.py::HTMLClassTestCase::test_url_match PASSED [ 94%] test/test_templating.py::HTMLClassTestCase::test_url_replace PASSED [ 94%] test/test_templating.py::MistuneTestCase::test_string_markdown PASSED [ 94%] test/test_templating.py::MistuneTestCase::test_string_markdown_code_block PASSED [ 94%] test/test_templating.py::MistuneTestCase::test_string_markdown_javascript_link PASSED [ 94%] test/test_templating.py::MistuneTestCase::test_string_markdown_link PASSED [ 94%] test/test_templating.py::Markdown2TestCase::test_string_markdown SKIPPED [ 94%] test/test_templating.py::Markdown2TestCase::test_string_markdown_code_block SKIPPED [ 94%] test/test_templating.py::Markdown2TestCase::test_string_markdown_javascript_link SKIPPED [ 94%] test/test_templating.py::Markdown2TestCase::test_string_markdown_link SKIPPED [ 94%] test/test_templating.py::MarkdownTestCase::test_string_markdown PASSED [ 95%] test/test_templating.py::MarkdownTestCase::test_string_markdown_code_block PASSED [ 95%] test/test_templating.py::MarkdownTestCase::test_string_markdown_javascript_link PASSED [ 95%] test/test_templating.py::MarkdownTestCase::test_string_markdown_link PASSED [ 95%] test/test_templating.py::NoMarkdownTestCase::test_string_markdown PASSED [ 95%] test/test_templating.py::NoRstTestCase::test_string_rst PASSED [ 95%] test/test_templating.py::NoStextTestCase::test_string_stext PASSED [ 95%] test/test_token.py::TokenTestCase::testBadQuote PASSED [ 95%] test/test_token.py::TokenTestCase::testEmbedQuote PASSED [ 95%] test/test_token.py::TokenTestCase::testEscaping PASSED [ 95%] test/test_token.py::TokenTestCase::testIgnoreExtraSpace PASSED [ 95%] test/test_token.py::TokenTestCase::testQuoting PASSED [ 95%] test/test_token.py::TokenTestCase::testValid PASSED [ 95%] test/test_userauditor.py::UserAuditorTest::testBadEmailAddresses PASSED [ 95%] test/test_userauditor.py::UserAuditorTest::testBadRoles PASSED [ 95%] test/test_userauditor.py::UserAuditorTest::testBadTimezones PASSED [ 96%] test/test_userauditor.py::UserAuditorTest::testBadTimezonesPyTZ PASSED [ 96%] test/test_userauditor.py::UserAuditorTest::testBadUsernames PASSED [ 96%] test/test_userauditor.py::UserAuditorTest::testGoodRoles PASSED [ 96%] test/test_userauditor.py::UserAuditorTest::testGoodTimezones PASSED [ 96%] test/test_userauditor.py::UserAuditorTest::testGoodTimezonesPyTZ PASSED [ 96%] test/test_userauditor.py::UserAuditorTest::testUniqueEmailAddresses PASSED [ 96%] test/test_xmlrpc.py::anydbmXmlrpcTest::testAccess PASSED [ 96%] test/test_xmlrpc.py::anydbmXmlrpcTest::testAction PASSED [ 96%] test/test_xmlrpc.py::anydbmXmlrpcTest::testAuthAllowedCreate PASSED [ 96%] test/test_xmlrpc.py::anydbmXmlrpcTest::testAuthAllowedEdit PASSED [ 96%] test/test_xmlrpc.py::anydbmXmlrpcTest::testAuthDeniedCreate PASSED [ 96%] test/test_xmlrpc.py::anydbmXmlrpcTest::testAuthDeniedEdit PASSED [ 96%] test/test_xmlrpc.py::anydbmXmlrpcTest::testAuthFilter PASSED [ 96%] test/test_xmlrpc.py::anydbmXmlrpcTest::testChange PASSED [ 97%] test/test_xmlrpc.py::anydbmXmlrpcTest::testCreate PASSED [ 97%] test/test_xmlrpc.py::anydbmXmlrpcTest::testFileCreate PASSED [ 97%] test/test_xmlrpc.py::anydbmXmlrpcTest::testLookup PASSED [ 97%] test/test_xmlrpc.py::anydbmXmlrpcTest::testMulticall PASSED [ 97%] test/test_xmlrpc.py::anydbmXmlrpcTest::testSchema PASSED [ 97%] test/test_xmlrpc.py::mysqlXmlrpcTest::testAccess SKIPPED (Skipping M...) [ 97%] test/test_xmlrpc.py::mysqlXmlrpcTest::testAction SKIPPED (Skipping M...) [ 97%] test/test_xmlrpc.py::mysqlXmlrpcTest::testAuthAllowedCreate SKIPPED [ 97%] test/test_xmlrpc.py::mysqlXmlrpcTest::testAuthAllowedEdit SKIPPED (S...) [ 97%] test/test_xmlrpc.py::mysqlXmlrpcTest::testAuthDeniedCreate SKIPPED (...) [ 97%] test/test_xmlrpc.py::mysqlXmlrpcTest::testAuthDeniedEdit SKIPPED (Sk...) [ 97%] test/test_xmlrpc.py::mysqlXmlrpcTest::testAuthFilter SKIPPED (Skippi...) [ 97%] test/test_xmlrpc.py::mysqlXmlrpcTest::testChange SKIPPED (Skipping M...) [ 97%] test/test_xmlrpc.py::mysqlXmlrpcTest::testCreate SKIPPED (Skipping M...) [ 97%] test/test_xmlrpc.py::mysqlXmlrpcTest::testFileCreate SKIPPED (Skippi...) [ 98%] test/test_xmlrpc.py::mysqlXmlrpcTest::testLookup SKIPPED (Skipping M...) [ 98%] test/test_xmlrpc.py::mysqlXmlrpcTest::testMulticall SKIPPED (Skippin...) [ 98%] test/test_xmlrpc.py::mysqlXmlrpcTest::testSchema SKIPPED (Skipping M...) [ 98%] test/test_xmlrpc.py::sqliteXmlrpcTest::testAccess PASSED [ 98%] test/test_xmlrpc.py::sqliteXmlrpcTest::testAction PASSED [ 98%] test/test_xmlrpc.py::sqliteXmlrpcTest::testAuthAllowedCreate PASSED [ 98%] test/test_xmlrpc.py::sqliteXmlrpcTest::testAuthAllowedEdit PASSED [ 98%] test/test_xmlrpc.py::sqliteXmlrpcTest::testAuthDeniedCreate PASSED [ 98%] test/test_xmlrpc.py::sqliteXmlrpcTest::testAuthDeniedEdit PASSED [ 98%] test/test_xmlrpc.py::sqliteXmlrpcTest::testAuthFilter PASSED [ 98%] test/test_xmlrpc.py::sqliteXmlrpcTest::testChange PASSED [ 98%] test/test_xmlrpc.py::sqliteXmlrpcTest::testCreate PASSED [ 98%] test/test_xmlrpc.py::sqliteXmlrpcTest::testFileCreate PASSED [ 98%] test/test_xmlrpc.py::sqliteXmlrpcTest::testLookup PASSED [ 98%] test/test_xmlrpc.py::sqliteXmlrpcTest::testMulticall PASSED [ 99%] test/test_xmlrpc.py::sqliteXmlrpcTest::testSchema PASSED [ 99%] test/test_xmlrpc.py::postgresqlXmlrpcTest::testAccess SKIPPED (Skipp...) [ 99%] test/test_xmlrpc.py::postgresqlXmlrpcTest::testAction SKIPPED (Skipp...) [ 99%] test/test_xmlrpc.py::postgresqlXmlrpcTest::testAuthAllowedCreate SKIPPED [ 99%] test/test_xmlrpc.py::postgresqlXmlrpcTest::testAuthAllowedEdit SKIPPED [ 99%] test/test_xmlrpc.py::postgresqlXmlrpcTest::testAuthDeniedCreate SKIPPED [ 99%] test/test_xmlrpc.py::postgresqlXmlrpcTest::testAuthDeniedEdit SKIPPED [ 99%] test/test_xmlrpc.py::postgresqlXmlrpcTest::testAuthFilter SKIPPED (S...) [ 99%] test/test_xmlrpc.py::postgresqlXmlrpcTest::testChange SKIPPED (Skipp...) [ 99%] test/test_xmlrpc.py::postgresqlXmlrpcTest::testCreate SKIPPED (Skipp...) [ 99%] test/test_xmlrpc.py::postgresqlXmlrpcTest::testFileCreate SKIPPED (S...) [ 99%] test/test_xmlrpc.py::postgresqlXmlrpcTest::testLookup SKIPPED (Skipp...) [ 99%] test/test_xmlrpc.py::postgresqlXmlrpcTest::testMulticall SKIPPED (Sk...) [ 99%] test/test_xmlrpc.py::postgresqlXmlrpcTest::testSchema SKIPPED (Skipp...) [100%] =================================== FAILURES =================================== ____________________ anydbmRestTest.testAcceptHeaderParsing ____________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523347, 'iat': 1622523287, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 53, 47, 357970, tzinfo=datetime.timezone.utc) expired_ts = 1622523227 jwt_perms = now_ts = 1622523287 otk = p = plus1min_ts = 1622523347 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyODcsImV4cCI6MTYyMjUyMzIyN30.PzRDhcYSnMJdAajd8c81CzlHqtLxtsgqZKzdbLi3GmM' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyODcsImV4cCI6MTYyMjUyMzIyN30.PzRDhcYSnMJdAajd8c81CzlHqtLxtsgqZKzdbLi3GmM' roundup/anypy/strings.py:21: AttributeError ______________________ anydbmRestTest.testAuthAllowedPost ______________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523347, 'iat': 1622523287, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 53, 47, 609268, tzinfo=datetime.timezone.utc) expired_ts = 1622523227 jwt_perms = now_ts = 1622523287 otk = p = plus1min_ts = 1622523347 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyODcsImV4cCI6MTYyMjUyMzIyN30.PzRDhcYSnMJdAajd8c81CzlHqtLxtsgqZKzdbLi3GmM' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyODcsImV4cCI6MTYyMjUyMzIyN30.PzRDhcYSnMJdAajd8c81CzlHqtLxtsgqZKzdbLi3GmM' roundup/anypy/strings.py:21: AttributeError ______________________ anydbmRestTest.testAuthAllowedPut _______________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523347, 'iat': 1622523287, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 53, 47, 824994, tzinfo=datetime.timezone.utc) expired_ts = 1622523227 jwt_perms = now_ts = 1622523287 otk = p = plus1min_ts = 1622523347 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyODcsImV4cCI6MTYyMjUyMzIyN30.PzRDhcYSnMJdAajd8c81CzlHqtLxtsgqZKzdbLi3GmM' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyODcsImV4cCI6MTYyMjUyMzIyN30.PzRDhcYSnMJdAajd8c81CzlHqtLxtsgqZKzdbLi3GmM' roundup/anypy/strings.py:21: AttributeError ______________________ anydbmRestTest.testAuthDeniedPost _______________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523348, 'iat': 1622523288, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 53, 48, 50406, tzinfo=datetime.timezone.utc) expired_ts = 1622523228 jwt_perms = now_ts = 1622523288 otk = p = plus1min_ts = 1622523348 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyODgsImV4cCI6MTYyMjUyMzIyOH0.WYDPvO74vqT-ZC6wChj6HqlGcfafX71DcibWhV8j9PM' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyODgsImV4cCI6MTYyMjUyMzIyOH0.WYDPvO74vqT-ZC6wChj6HqlGcfafX71DcibWhV8j9PM' roundup/anypy/strings.py:21: AttributeError _______________________ anydbmRestTest.testAuthDeniedPut _______________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523348, 'iat': 1622523288, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 53, 48, 288017, tzinfo=datetime.timezone.utc) expired_ts = 1622523228 jwt_perms = now_ts = 1622523288 otk = p = plus1min_ts = 1622523348 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyODgsImV4cCI6MTYyMjUyMzIyOH0.WYDPvO74vqT-ZC6wChj6HqlGcfafX71DcibWhV8j9PM' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyODgsImV4cCI6MTYyMjUyMzIyOH0.WYDPvO74vqT-ZC6wChj6HqlGcfafX71DcibWhV8j9PM' roundup/anypy/strings.py:21: AttributeError ____________________ anydbmRestTest.testBinaryFieldStorage _____________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523348, 'iat': 1622523288, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 53, 48, 640050, tzinfo=datetime.timezone.utc) expired_ts = 1622523228 jwt_perms = now_ts = 1622523288 otk = p = plus1min_ts = 1622523348 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyODgsImV4cCI6MTYyMjUyMzIyOH0.WYDPvO74vqT-ZC6wChj6HqlGcfafX71DcibWhV8j9PM' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyODgsImV4cCI6MTYyMjUyMzIyOH0.WYDPvO74vqT-ZC6wChj6HqlGcfafX71DcibWhV8j9PM' roundup/anypy/strings.py:21: AttributeError ____________________ anydbmRestTest.testDeleteAttributeUri _____________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523348, 'iat': 1622523288, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 53, 48, 929322, tzinfo=datetime.timezone.utc) expired_ts = 1622523228 jwt_perms = now_ts = 1622523288 otk = p = plus1min_ts = 1622523348 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyODgsImV4cCI6MTYyMjUyMzIyOH0.WYDPvO74vqT-ZC6wChj6HqlGcfafX71DcibWhV8j9PM' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyODgsImV4cCI6MTYyMjUyMzIyOH0.WYDPvO74vqT-ZC6wChj6HqlGcfafX71DcibWhV8j9PM' roundup/anypy/strings.py:21: AttributeError _________________________ anydbmRestTest.testDispatch __________________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523349, 'iat': 1622523289, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 53, 49, 211801, tzinfo=datetime.timezone.utc) expired_ts = 1622523229 jwt_perms = now_ts = 1622523289 otk = p = plus1min_ts = 1622523349 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyODksImV4cCI6MTYyMjUyMzIyOX0.HaWx3GY_maTYAryGZdzJeL9_S1XxF3LEE-RySjHSN7w' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyODksImV4cCI6MTYyMjUyMzIyOX0.HaWx3GY_maTYAryGZdzJeL9_S1XxF3LEE-RySjHSN7w' roundup/anypy/strings.py:21: AttributeError _______________________ anydbmRestTest.testDispatchPost ________________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523349, 'iat': 1622523289, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 53, 49, 706214, tzinfo=datetime.timezone.utc) expired_ts = 1622523229 jwt_perms = now_ts = 1622523289 otk = p = plus1min_ts = 1622523349 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyODksImV4cCI6MTYyMjUyMzIyOX0.HaWx3GY_maTYAryGZdzJeL9_S1XxF3LEE-RySjHSN7w' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyODksImV4cCI6MTYyMjUyMzIyOX0.HaWx3GY_maTYAryGZdzJeL9_S1XxF3LEE-RySjHSN7w' roundup/anypy/strings.py:21: AttributeError ______________________ anydbmRestTest.testEtagGeneration _______________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523349, 'iat': 1622523289, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 53, 49, 907569, tzinfo=datetime.timezone.utc) expired_ts = 1622523229 jwt_perms = now_ts = 1622523289 otk = p = plus1min_ts = 1622523349 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyODksImV4cCI6MTYyMjUyMzIyOX0.HaWx3GY_maTYAryGZdzJeL9_S1XxF3LEE-RySjHSN7w' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyODksImV4cCI6MTYyMjUyMzIyOX0.HaWx3GY_maTYAryGZdzJeL9_S1XxF3LEE-RySjHSN7w' roundup/anypy/strings.py:21: AttributeError ______________________ anydbmRestTest.testEtagProcessing _______________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523350, 'iat': 1622523290, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 53, 50, 186176, tzinfo=datetime.timezone.utc) expired_ts = 1622523230 jwt_perms = now_ts = 1622523290 otk = p = plus1min_ts = 1622523350 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyOTAsImV4cCI6MTYyMjUyMzIzMH0.zACikK5p4XuMXCacNOij8jdoVmathyhfoXTLG1t22e0' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyOTAsImV4cCI6MTYyMjUyMzIzMH0.zACikK5p4XuMXCacNOij8jdoVmathyhfoXTLG1t22e0' roundup/anypy/strings.py:21: AttributeError __________________________ anydbmRestTest.testFilter ___________________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523350, 'iat': 1622523290, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 53, 50, 381362, tzinfo=datetime.timezone.utc) expired_ts = 1622523230 jwt_perms = now_ts = 1622523290 otk = p = plus1min_ts = 1622523350 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyOTAsImV4cCI6MTYyMjUyMzIzMH0.zACikK5p4XuMXCacNOij8jdoVmathyhfoXTLG1t22e0' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyOTAsImV4cCI6MTYyMjUyMzIzMH0.zACikK5p4XuMXCacNOij8jdoVmathyhfoXTLG1t22e0' roundup/anypy/strings.py:21: AttributeError ____________________________ anydbmRestTest.testGet ____________________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523350, 'iat': 1622523290, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 53, 50, 554552, tzinfo=datetime.timezone.utc) expired_ts = 1622523230 jwt_perms = now_ts = 1622523290 otk = p = plus1min_ts = 1622523350 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyOTAsImV4cCI6MTYyMjUyMzIzMH0.zACikK5p4XuMXCacNOij8jdoVmathyhfoXTLG1t22e0' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyOTAsImV4cCI6MTYyMjUyMzIzMH0.zACikK5p4XuMXCacNOij8jdoVmathyhfoXTLG1t22e0' roundup/anypy/strings.py:21: AttributeError _______________________ anydbmRestTest.testGetExactMatch _______________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523350, 'iat': 1622523290, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 53, 50, 810764, tzinfo=datetime.timezone.utc) expired_ts = 1622523230 jwt_perms = now_ts = 1622523290 otk = p = plus1min_ts = 1622523350 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyOTAsImV4cCI6MTYyMjUyMzIzMH0.zACikK5p4XuMXCacNOij8jdoVmathyhfoXTLG1t22e0' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyOTAsImV4cCI6MTYyMjUyMzIzMH0.zACikK5p4XuMXCacNOij8jdoVmathyhfoXTLG1t22e0' roundup/anypy/strings.py:21: AttributeError _______________________ anydbmRestTest.testGetTransitive _______________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523350, 'iat': 1622523290, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 53, 50, 996962, tzinfo=datetime.timezone.utc) expired_ts = 1622523230 jwt_perms = now_ts = 1622523290 otk = p = plus1min_ts = 1622523350 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyOTAsImV4cCI6MTYyMjUyMzIzMH0.zACikK5p4XuMXCacNOij8jdoVmathyhfoXTLG1t22e0' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyOTAsImV4cCI6MTYyMjUyMzIzMH0.zACikK5p4XuMXCacNOij8jdoVmathyhfoXTLG1t22e0' roundup/anypy/strings.py:21: AttributeError ______________________ anydbmRestTest.testMethodOverride _______________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523351, 'iat': 1622523291, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 53, 51, 251643, tzinfo=datetime.timezone.utc) expired_ts = 1622523231 jwt_perms = now_ts = 1622523291 otk = p = plus1min_ts = 1622523351 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyOTEsImV4cCI6MTYyMjUyMzIzMX0.csuir5ytBdevv57l6N-rB-nIu65z7-XblmHLQGCk99s' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyOTEsImV4cCI6MTYyMjUyMzIzMX0.csuir5ytBdevv57l6N-rB-nIu65z7-XblmHLQGCk99s' roundup/anypy/strings.py:21: AttributeError _______________________ anydbmRestTest.testOutputFormat ________________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523351, 'iat': 1622523291, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 53, 51, 426457, tzinfo=datetime.timezone.utc) expired_ts = 1622523231 jwt_perms = now_ts = 1622523291 otk = p = plus1min_ts = 1622523351 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyOTEsImV4cCI6MTYyMjUyMzIzMX0.csuir5ytBdevv57l6N-rB-nIu65z7-XblmHLQGCk99s' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyOTEsImV4cCI6MTYyMjUyMzIzMX0.csuir5ytBdevv57l6N-rB-nIu65z7-XblmHLQGCk99s' roundup/anypy/strings.py:21: AttributeError ________________________ anydbmRestTest.testPagination _________________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523351, 'iat': 1622523291, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 53, 51, 632985, tzinfo=datetime.timezone.utc) expired_ts = 1622523231 jwt_perms = now_ts = 1622523291 otk = p = plus1min_ts = 1622523351 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyOTEsImV4cCI6MTYyMjUyMzIzMX0.csuir5ytBdevv57l6N-rB-nIu65z7-XblmHLQGCk99s' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyOTEsImV4cCI6MTYyMjUyMzIzMX0.csuir5ytBdevv57l6N-rB-nIu65z7-XblmHLQGCk99s' roundup/anypy/strings.py:21: AttributeError ________________________ anydbmRestTest.testPatchAction ________________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523351, 'iat': 1622523291, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 53, 51, 862074, tzinfo=datetime.timezone.utc) expired_ts = 1622523231 jwt_perms = now_ts = 1622523291 otk = p = plus1min_ts = 1622523351 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyOTEsImV4cCI6MTYyMjUyMzIzMX0.csuir5ytBdevv57l6N-rB-nIu65z7-XblmHLQGCk99s' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyOTEsImV4cCI6MTYyMjUyMzIzMX0.csuir5ytBdevv57l6N-rB-nIu65z7-XblmHLQGCk99s' roundup/anypy/strings.py:21: AttributeError _________________________ anydbmRestTest.testPatchAdd __________________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523352, 'iat': 1622523292, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 53, 52, 32560, tzinfo=datetime.timezone.utc) expired_ts = 1622523232 jwt_perms = now_ts = 1622523292 otk = p = plus1min_ts = 1622523352 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyOTIsImV4cCI6MTYyMjUyMzIzMn0.aCKAetCZJsPE1NLIEXZN0qPFM2fIqY2HxObP_sM2xKg' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyOTIsImV4cCI6MTYyMjUyMzIzMn0.aCKAetCZJsPE1NLIEXZN0qPFM2fIqY2HxObP_sM2xKg' roundup/anypy/strings.py:21: AttributeError ________________________ anydbmRestTest.testPatchRemove ________________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523352, 'iat': 1622523292, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 53, 52, 277640, tzinfo=datetime.timezone.utc) expired_ts = 1622523232 jwt_perms = now_ts = 1622523292 otk = p = plus1min_ts = 1622523352 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyOTIsImV4cCI6MTYyMjUyMzIzMn0.aCKAetCZJsPE1NLIEXZN0qPFM2fIqY2HxObP_sM2xKg' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyOTIsImV4cCI6MTYyMjUyMzIzMn0.aCKAetCZJsPE1NLIEXZN0qPFM2fIqY2HxObP_sM2xKg' roundup/anypy/strings.py:21: AttributeError ______________________ anydbmRestTest.testPatchRemoveAll _______________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523352, 'iat': 1622523292, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 53, 52, 474411, tzinfo=datetime.timezone.utc) expired_ts = 1622523232 jwt_perms = now_ts = 1622523292 otk = p = plus1min_ts = 1622523352 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyOTIsImV4cCI6MTYyMjUyMzIzMn0.aCKAetCZJsPE1NLIEXZN0qPFM2fIqY2HxObP_sM2xKg' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyOTIsImV4cCI6MTYyMjUyMzIzMn0.aCKAetCZJsPE1NLIEXZN0qPFM2fIqY2HxObP_sM2xKg' roundup/anypy/strings.py:21: AttributeError _______________________ anydbmRestTest.testPatchReplace ________________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523352, 'iat': 1622523292, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 53, 52, 730085, tzinfo=datetime.timezone.utc) expired_ts = 1622523232 jwt_perms = now_ts = 1622523292 otk = p = plus1min_ts = 1622523352 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyOTIsImV4cCI6MTYyMjUyMzIzMn0.aCKAetCZJsPE1NLIEXZN0qPFM2fIqY2HxObP_sM2xKg' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyOTIsImV4cCI6MTYyMjUyMzIzMn0.aCKAetCZJsPE1NLIEXZN0qPFM2fIqY2HxObP_sM2xKg' roundup/anypy/strings.py:21: AttributeError ___________________________ anydbmRestTest.testPost ____________________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523352, 'iat': 1622523292, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 53, 52, 912533, tzinfo=datetime.timezone.utc) expired_ts = 1622523232 jwt_perms = now_ts = 1622523292 otk = p = plus1min_ts = 1622523352 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyOTIsImV4cCI6MTYyMjUyMzIzMn0.aCKAetCZJsPE1NLIEXZN0qPFM2fIqY2HxObP_sM2xKg' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyOTIsImV4cCI6MTYyMjUyMzIzMn0.aCKAetCZJsPE1NLIEXZN0qPFM2fIqY2HxObP_sM2xKg' roundup/anypy/strings.py:21: AttributeError _________________________ anydbmRestTest.testPostFile __________________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523353, 'iat': 1622523293, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 53, 53, 103396, tzinfo=datetime.timezone.utc) expired_ts = 1622523233 jwt_perms = now_ts = 1622523293 otk = p = plus1min_ts = 1622523353 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyOTMsImV4cCI6MTYyMjUyMzIzM30.3xWHzhe8nSuBR4oKluL6M7i41n7CyhXhjuNlKZaMO3g' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyOTMsImV4cCI6MTYyMjUyMzIzM30.3xWHzhe8nSuBR4oKluL6M7i41n7CyhXhjuNlKZaMO3g' roundup/anypy/strings.py:21: AttributeError __________________________ anydbmRestTest.testPostPOE __________________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523353, 'iat': 1622523293, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 53, 53, 379787, tzinfo=datetime.timezone.utc) expired_ts = 1622523233 jwt_perms = now_ts = 1622523293 otk = p = plus1min_ts = 1622523353 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyOTMsImV4cCI6MTYyMjUyMzIzM30.3xWHzhe8nSuBR4oKluL6M7i41n7CyhXhjuNlKZaMO3g' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyOTMsImV4cCI6MTYyMjUyMzIzM30.3xWHzhe8nSuBR4oKluL6M7i41n7CyhXhjuNlKZaMO3g' roundup/anypy/strings.py:21: AttributeError _______________________ anydbmRestTest.testPutAttribute ________________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523353, 'iat': 1622523293, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 53, 53, 566780, tzinfo=datetime.timezone.utc) expired_ts = 1622523233 jwt_perms = now_ts = 1622523293 otk = p = plus1min_ts = 1622523353 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyOTMsImV4cCI6MTYyMjUyMzIzM30.3xWHzhe8nSuBR4oKluL6M7i41n7CyhXhjuNlKZaMO3g' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyOTMsImV4cCI6MTYyMjUyMzIzM30.3xWHzhe8nSuBR4oKluL6M7i41n7CyhXhjuNlKZaMO3g' roundup/anypy/strings.py:21: AttributeError ________________________ anydbmRestTest.testPutElement _________________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523353, 'iat': 1622523293, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 53, 53, 808620, tzinfo=datetime.timezone.utc) expired_ts = 1622523233 jwt_perms = now_ts = 1622523293 otk = p = plus1min_ts = 1622523353 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyOTMsImV4cCI6MTYyMjUyMzIzM30.3xWHzhe8nSuBR4oKluL6M7i41n7CyhXhjuNlKZaMO3g' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyOTMsImV4cCI6MTYyMjUyMzIzM30.3xWHzhe8nSuBR4oKluL6M7i41n7CyhXhjuNlKZaMO3g' roundup/anypy/strings.py:21: AttributeError _______________________ anydbmRestTest.testRestRateLimit _______________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523353, 'iat': 1622523293, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 53, 53, 988617, tzinfo=datetime.timezone.utc) expired_ts = 1622523233 jwt_perms = now_ts = 1622523293 otk = p = plus1min_ts = 1622523353 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyOTMsImV4cCI6MTYyMjUyMzIzM30.3xWHzhe8nSuBR4oKluL6M7i41n7CyhXhjuNlKZaMO3g' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyOTMsImV4cCI6MTYyMjUyMzIzM30.3xWHzhe8nSuBR4oKluL6M7i41n7CyhXhjuNlKZaMO3g' roundup/anypy/strings.py:21: AttributeError __________________________ anydbmRestTest.testSorting __________________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523354, 'iat': 1622523294, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 53, 54, 175951, tzinfo=datetime.timezone.utc) expired_ts = 1622523234 jwt_perms = now_ts = 1622523294 otk = p = plus1min_ts = 1622523354 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyOTQsImV4cCI6MTYyMjUyMzIzNH0.ZgLZq8yZioGYgEawjsMiDKXf1K4vElPHL7sIFtLffvo' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyOTQsImV4cCI6MTYyMjUyMzIzNH0.ZgLZq8yZioGYgEawjsMiDKXf1K4vElPHL7sIFtLffvo' roundup/anypy/strings.py:21: AttributeError _________________________ anydbmRestTest.testStatsGen __________________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523354, 'iat': 1622523294, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 53, 54, 448645, tzinfo=datetime.timezone.utc) expired_ts = 1622523234 jwt_perms = now_ts = 1622523294 otk = p = plus1min_ts = 1622523354 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyOTQsImV4cCI6MTYyMjUyMzIzNH0.ZgLZq8yZioGYgEawjsMiDKXf1K4vElPHL7sIFtLffvo' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyOTQsImV4cCI6MTYyMjUyMzIzNH0.ZgLZq8yZioGYgEawjsMiDKXf1K4vElPHL7sIFtLffvo' roundup/anypy/strings.py:21: AttributeError ______________________ anydbmRestTest.testTransitiveField ______________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523354, 'iat': 1622523294, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 53, 54, 625173, tzinfo=datetime.timezone.utc) expired_ts = 1622523234 jwt_perms = now_ts = 1622523294 otk = p = plus1min_ts = 1622523354 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyOTQsImV4cCI6MTYyMjUyMzIzNH0.ZgLZq8yZioGYgEawjsMiDKXf1K4vElPHL7sIFtLffvo' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyOTQsImV4cCI6MTYyMjUyMzIzNH0.ZgLZq8yZioGYgEawjsMiDKXf1K4vElPHL7sIFtLffvo' roundup/anypy/strings.py:21: AttributeError _____________________ anydbmRestTest.test_bad_audience_jwt _____________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523354, 'iat': 1622523294, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 53, 54, 879928, tzinfo=datetime.timezone.utc) expired_ts = 1622523234 jwt_perms = now_ts = 1622523294 otk = p = plus1min_ts = 1622523354 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyOTQsImV4cCI6MTYyMjUyMzIzNH0.ZgLZq8yZioGYgEawjsMiDKXf1K4vElPHL7sIFtLffvo' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyOTQsImV4cCI6MTYyMjUyMzIzNH0.ZgLZq8yZioGYgEawjsMiDKXf1K4vElPHL7sIFtLffvo' roundup/anypy/strings.py:21: AttributeError ______________________ anydbmRestTest.test_bad_issue_jwt _______________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523355, 'iat': 1622523295, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 53, 55, 58006, tzinfo=datetime.timezone.utc) expired_ts = 1622523235 jwt_perms = now_ts = 1622523295 otk = p = plus1min_ts = 1622523355 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyOTUsImV4cCI6MTYyMjUyMzIzNX0.zEqJaOsiuKHmoPSnvflUbJFz6qCDLjBqhsH7yo8T4YI' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyOTUsImV4cCI6MTYyMjUyMzIzNX0.zEqJaOsiuKHmoPSnvflUbJFz6qCDLjBqhsH7yo8T4YI' roundup/anypy/strings.py:21: AttributeError ______________________ anydbmRestTest.test_bad_roles_jwt _______________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523355, 'iat': 1622523295, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 53, 55, 240408, tzinfo=datetime.timezone.utc) expired_ts = 1622523235 jwt_perms = now_ts = 1622523295 otk = p = plus1min_ts = 1622523355 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyOTUsImV4cCI6MTYyMjUyMzIzNX0.zEqJaOsiuKHmoPSnvflUbJFz6qCDLjBqhsH7yo8T4YI' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyOTUsImV4cCI6MTYyMjUyMzIzNX0.zEqJaOsiuKHmoPSnvflUbJFz6qCDLjBqhsH7yo8T4YI' roundup/anypy/strings.py:21: AttributeError _____________________ anydbmRestTest.test_bad_subject_jwt ______________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523355, 'iat': 1622523295, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 53, 55, 485997, tzinfo=datetime.timezone.utc) expired_ts = 1622523235 jwt_perms = now_ts = 1622523295 otk = p = plus1min_ts = 1622523355 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyOTUsImV4cCI6MTYyMjUyMzIzNX0.zEqJaOsiuKHmoPSnvflUbJFz6qCDLjBqhsH7yo8T4YI' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyOTUsImV4cCI6MTYyMjUyMzIzNX0.zEqJaOsiuKHmoPSnvflUbJFz6qCDLjBqhsH7yo8T4YI' roundup/anypy/strings.py:21: AttributeError _______________________ anydbmRestTest.test_disabled_jwt _______________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523355, 'iat': 1622523295, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 53, 55, 691603, tzinfo=datetime.timezone.utc) expired_ts = 1622523235 jwt_perms = now_ts = 1622523295 otk = p = plus1min_ts = 1622523355 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyOTUsImV4cCI6MTYyMjUyMzIzNX0.zEqJaOsiuKHmoPSnvflUbJFz6qCDLjBqhsH7yo8T4YI' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyOTUsImV4cCI6MTYyMjUyMzIzNX0.zEqJaOsiuKHmoPSnvflUbJFz6qCDLjBqhsH7yo8T4YI' roundup/anypy/strings.py:21: AttributeError _______________________ anydbmRestTest.test_expired_jwt ________________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523355, 'iat': 1622523295, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 53, 55, 951828, tzinfo=datetime.timezone.utc) expired_ts = 1622523235 jwt_perms = now_ts = 1622523295 otk = p = plus1min_ts = 1622523355 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyOTUsImV4cCI6MTYyMjUyMzIzNX0.zEqJaOsiuKHmoPSnvflUbJFz6qCDLjBqhsH7yo8T4YI' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyOTUsImV4cCI6MTYyMjUyMzIzNX0.zEqJaOsiuKHmoPSnvflUbJFz6qCDLjBqhsH7yo8T4YI' roundup/anypy/strings.py:21: AttributeError ______________________ anydbmRestTest.test_user_email_jwt ______________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523356, 'iat': 1622523296, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 53, 56, 184180, tzinfo=datetime.timezone.utc) expired_ts = 1622523236 jwt_perms = now_ts = 1622523296 otk = p = plus1min_ts = 1622523356 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyOTYsImV4cCI6MTYyMjUyMzIzNn0.zT5PsR9ezWxFJzPZQ28uAfA6bjJN0ioXeeCEm4x9ab4' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyOTYsImV4cCI6MTYyMjUyMzIzNn0.zT5PsR9ezWxFJzPZQ28uAfA6bjJN0ioXeeCEm4x9ab4' roundup/anypy/strings.py:21: AttributeError ___________________ anydbmRestTest.test_user_emailnorest_jwt ___________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523356, 'iat': 1622523296, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 53, 56, 380067, tzinfo=datetime.timezone.utc) expired_ts = 1622523236 jwt_perms = now_ts = 1622523296 otk = p = plus1min_ts = 1622523356 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyOTYsImV4cCI6MTYyMjUyMzIzNn0.zT5PsR9ezWxFJzPZQ28uAfA6bjJN0ioXeeCEm4x9ab4' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyOTYsImV4cCI6MTYyMjUyMzIzNn0.zT5PsR9ezWxFJzPZQ28uAfA6bjJN0ioXeeCEm4x9ab4' roundup/anypy/strings.py:21: AttributeError _________________________ anydbmRestTest.test_user_jwt _________________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523356, 'iat': 1622523296, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 53, 56, 642557, tzinfo=datetime.timezone.utc) expired_ts = 1622523236 jwt_perms = now_ts = 1622523296 otk = p = plus1min_ts = 1622523356 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyOTYsImV4cCI6MTYyMjUyMzIzNn0.zT5PsR9ezWxFJzPZQ28uAfA6bjJN0ioXeeCEm4x9ab4' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMyOTYsImV4cCI6MTYyMjUyMzIzNn0.zT5PsR9ezWxFJzPZQ28uAfA6bjJN0ioXeeCEm4x9ab4' roundup/anypy/strings.py:21: AttributeError ____________________ sqliteRestTest.testAcceptHeaderParsing ____________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523421, 'iat': 1622523361, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 55, 1, 685336, tzinfo=datetime.timezone.utc) expired_ts = 1622523301 jwt_perms = now_ts = 1622523361 otk = p = plus1min_ts = 1622523421 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjEsImV4cCI6MTYyMjUyMzMwMX0.Ulo0LUlTLbPh4F8MS-y_WHSN7FdhrA_XE7bBEymh_1U' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjEsImV4cCI6MTYyMjUyMzMwMX0.Ulo0LUlTLbPh4F8MS-y_WHSN7FdhrA_XE7bBEymh_1U' roundup/anypy/strings.py:21: AttributeError ______________________ sqliteRestTest.testAuthAllowedPost ______________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523422, 'iat': 1622523362, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 55, 2, 106807, tzinfo=datetime.timezone.utc) expired_ts = 1622523302 jwt_perms = now_ts = 1622523362 otk = p = plus1min_ts = 1622523422 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjIsImV4cCI6MTYyMjUyMzMwMn0.LSSvxeztxaU9fDb6aL4Dc7Xy_3vweyDddl1xqOmXXBg' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjIsImV4cCI6MTYyMjUyMzMwMn0.LSSvxeztxaU9fDb6aL4Dc7Xy_3vweyDddl1xqOmXXBg' roundup/anypy/strings.py:21: AttributeError ______________________ sqliteRestTest.testAuthAllowedPut _______________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523422, 'iat': 1622523362, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 55, 2, 312462, tzinfo=datetime.timezone.utc) expired_ts = 1622523302 jwt_perms = now_ts = 1622523362 otk = p = plus1min_ts = 1622523422 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjIsImV4cCI6MTYyMjUyMzMwMn0.LSSvxeztxaU9fDb6aL4Dc7Xy_3vweyDddl1xqOmXXBg' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjIsImV4cCI6MTYyMjUyMzMwMn0.LSSvxeztxaU9fDb6aL4Dc7Xy_3vweyDddl1xqOmXXBg' roundup/anypy/strings.py:21: AttributeError ______________________ sqliteRestTest.testAuthDeniedPost _______________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523422, 'iat': 1622523362, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 55, 2, 511015, tzinfo=datetime.timezone.utc) expired_ts = 1622523302 jwt_perms = now_ts = 1622523362 otk = p = plus1min_ts = 1622523422 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjIsImV4cCI6MTYyMjUyMzMwMn0.LSSvxeztxaU9fDb6aL4Dc7Xy_3vweyDddl1xqOmXXBg' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjIsImV4cCI6MTYyMjUyMzMwMn0.LSSvxeztxaU9fDb6aL4Dc7Xy_3vweyDddl1xqOmXXBg' roundup/anypy/strings.py:21: AttributeError _______________________ sqliteRestTest.testAuthDeniedPut _______________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523422, 'iat': 1622523362, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 55, 2, 803468, tzinfo=datetime.timezone.utc) expired_ts = 1622523302 jwt_perms = now_ts = 1622523362 otk = p = plus1min_ts = 1622523422 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjIsImV4cCI6MTYyMjUyMzMwMn0.LSSvxeztxaU9fDb6aL4Dc7Xy_3vweyDddl1xqOmXXBg' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjIsImV4cCI6MTYyMjUyMzMwMn0.LSSvxeztxaU9fDb6aL4Dc7Xy_3vweyDddl1xqOmXXBg' roundup/anypy/strings.py:21: AttributeError ____________________ sqliteRestTest.testBinaryFieldStorage _____________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523422, 'iat': 1622523362, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 55, 2, 997613, tzinfo=datetime.timezone.utc) expired_ts = 1622523302 jwt_perms = now_ts = 1622523362 otk = p = plus1min_ts = 1622523422 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjIsImV4cCI6MTYyMjUyMzMwMn0.LSSvxeztxaU9fDb6aL4Dc7Xy_3vweyDddl1xqOmXXBg' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjIsImV4cCI6MTYyMjUyMzMwMn0.LSSvxeztxaU9fDb6aL4Dc7Xy_3vweyDddl1xqOmXXBg' roundup/anypy/strings.py:21: AttributeError ____________________ sqliteRestTest.testDeleteAttributeUri _____________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523423, 'iat': 1622523363, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 55, 3, 322424, tzinfo=datetime.timezone.utc) expired_ts = 1622523303 jwt_perms = now_ts = 1622523363 otk = p = plus1min_ts = 1622523423 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjMsImV4cCI6MTYyMjUyMzMwM30.kE_jpokxRVhf3-s2Zu7vo2VwbeRQ3Kmykj7sZfYJpvY' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjMsImV4cCI6MTYyMjUyMzMwM30.kE_jpokxRVhf3-s2Zu7vo2VwbeRQ3Kmykj7sZfYJpvY' roundup/anypy/strings.py:21: AttributeError _________________________ sqliteRestTest.testDispatch __________________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523423, 'iat': 1622523363, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 55, 3, 633946, tzinfo=datetime.timezone.utc) expired_ts = 1622523303 jwt_perms = now_ts = 1622523363 otk = p = plus1min_ts = 1622523423 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjMsImV4cCI6MTYyMjUyMzMwM30.kE_jpokxRVhf3-s2Zu7vo2VwbeRQ3Kmykj7sZfYJpvY' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjMsImV4cCI6MTYyMjUyMzMwM30.kE_jpokxRVhf3-s2Zu7vo2VwbeRQ3Kmykj7sZfYJpvY' roundup/anypy/strings.py:21: AttributeError _______________________ sqliteRestTest.testDispatchPost ________________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523423, 'iat': 1622523363, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 55, 3, 840511, tzinfo=datetime.timezone.utc) expired_ts = 1622523303 jwt_perms = now_ts = 1622523363 otk = p = plus1min_ts = 1622523423 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjMsImV4cCI6MTYyMjUyMzMwM30.kE_jpokxRVhf3-s2Zu7vo2VwbeRQ3Kmykj7sZfYJpvY' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjMsImV4cCI6MTYyMjUyMzMwM30.kE_jpokxRVhf3-s2Zu7vo2VwbeRQ3Kmykj7sZfYJpvY' roundup/anypy/strings.py:21: AttributeError ______________________ sqliteRestTest.testEtagGeneration _______________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523424, 'iat': 1622523364, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 55, 4, 181630, tzinfo=datetime.timezone.utc) expired_ts = 1622523304 jwt_perms = now_ts = 1622523364 otk = p = plus1min_ts = 1622523424 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjQsImV4cCI6MTYyMjUyMzMwNH0.BNygqTKUzHhn3uYRcti96BCJIVTZaRZe-AjEboRt9pk' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjQsImV4cCI6MTYyMjUyMzMwNH0.BNygqTKUzHhn3uYRcti96BCJIVTZaRZe-AjEboRt9pk' roundup/anypy/strings.py:21: AttributeError ______________________ sqliteRestTest.testEtagProcessing _______________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523424, 'iat': 1622523364, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 55, 4, 428480, tzinfo=datetime.timezone.utc) expired_ts = 1622523304 jwt_perms = now_ts = 1622523364 otk = p = plus1min_ts = 1622523424 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjQsImV4cCI6MTYyMjUyMzMwNH0.BNygqTKUzHhn3uYRcti96BCJIVTZaRZe-AjEboRt9pk' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjQsImV4cCI6MTYyMjUyMzMwNH0.BNygqTKUzHhn3uYRcti96BCJIVTZaRZe-AjEboRt9pk' roundup/anypy/strings.py:21: AttributeError __________________________ sqliteRestTest.testFilter ___________________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523424, 'iat': 1622523364, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 55, 4, 770670, tzinfo=datetime.timezone.utc) expired_ts = 1622523304 jwt_perms = now_ts = 1622523364 otk = p = plus1min_ts = 1622523424 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjQsImV4cCI6MTYyMjUyMzMwNH0.BNygqTKUzHhn3uYRcti96BCJIVTZaRZe-AjEboRt9pk' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjQsImV4cCI6MTYyMjUyMzMwNH0.BNygqTKUzHhn3uYRcti96BCJIVTZaRZe-AjEboRt9pk' roundup/anypy/strings.py:21: AttributeError ____________________________ sqliteRestTest.testGet ____________________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523425, 'iat': 1622523365, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 55, 5, 25966, tzinfo=datetime.timezone.utc) expired_ts = 1622523305 jwt_perms = now_ts = 1622523365 otk = p = plus1min_ts = 1622523425 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjUsImV4cCI6MTYyMjUyMzMwNX0.ZVLxuC6SOphOFtPLKeTxAwtwEu8VYsu29K1fE-vUuxY' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjUsImV4cCI6MTYyMjUyMzMwNX0.ZVLxuC6SOphOFtPLKeTxAwtwEu8VYsu29K1fE-vUuxY' roundup/anypy/strings.py:21: AttributeError _______________________ sqliteRestTest.testGetExactMatch _______________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523425, 'iat': 1622523365, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 55, 5, 229291, tzinfo=datetime.timezone.utc) expired_ts = 1622523305 jwt_perms = now_ts = 1622523365 otk = p = plus1min_ts = 1622523425 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjUsImV4cCI6MTYyMjUyMzMwNX0.ZVLxuC6SOphOFtPLKeTxAwtwEu8VYsu29K1fE-vUuxY' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjUsImV4cCI6MTYyMjUyMzMwNX0.ZVLxuC6SOphOFtPLKeTxAwtwEu8VYsu29K1fE-vUuxY' roundup/anypy/strings.py:21: AttributeError _______________________ sqliteRestTest.testGetTransitive _______________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523425, 'iat': 1622523365, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 55, 5, 512779, tzinfo=datetime.timezone.utc) expired_ts = 1622523305 jwt_perms = now_ts = 1622523365 otk = p = plus1min_ts = 1622523425 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjUsImV4cCI6MTYyMjUyMzMwNX0.ZVLxuC6SOphOFtPLKeTxAwtwEu8VYsu29K1fE-vUuxY' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjUsImV4cCI6MTYyMjUyMzMwNX0.ZVLxuC6SOphOFtPLKeTxAwtwEu8VYsu29K1fE-vUuxY' roundup/anypy/strings.py:21: AttributeError ______________________ sqliteRestTest.testMethodOverride _______________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523425, 'iat': 1622523365, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 55, 5, 699628, tzinfo=datetime.timezone.utc) expired_ts = 1622523305 jwt_perms = now_ts = 1622523365 otk = p = plus1min_ts = 1622523425 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjUsImV4cCI6MTYyMjUyMzMwNX0.ZVLxuC6SOphOFtPLKeTxAwtwEu8VYsu29K1fE-vUuxY' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjUsImV4cCI6MTYyMjUyMzMwNX0.ZVLxuC6SOphOFtPLKeTxAwtwEu8VYsu29K1fE-vUuxY' roundup/anypy/strings.py:21: AttributeError _______________________ sqliteRestTest.testOutputFormat ________________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523426, 'iat': 1622523366, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 55, 6, 3499, tzinfo=datetime.timezone.utc) expired_ts = 1622523306 jwt_perms = now_ts = 1622523366 otk = p = plus1min_ts = 1622523426 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjYsImV4cCI6MTYyMjUyMzMwNn0.hHYD9aUeBiFeIo09T5XD_c2b8vath1P3xpntzNtj4HU' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjYsImV4cCI6MTYyMjUyMzMwNn0.hHYD9aUeBiFeIo09T5XD_c2b8vath1P3xpntzNtj4HU' roundup/anypy/strings.py:21: AttributeError ________________________ sqliteRestTest.testPagination _________________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523426, 'iat': 1622523366, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 55, 6, 196696, tzinfo=datetime.timezone.utc) expired_ts = 1622523306 jwt_perms = now_ts = 1622523366 otk = p = plus1min_ts = 1622523426 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjYsImV4cCI6MTYyMjUyMzMwNn0.hHYD9aUeBiFeIo09T5XD_c2b8vath1P3xpntzNtj4HU' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjYsImV4cCI6MTYyMjUyMzMwNn0.hHYD9aUeBiFeIo09T5XD_c2b8vath1P3xpntzNtj4HU' roundup/anypy/strings.py:21: AttributeError ________________________ sqliteRestTest.testPatchAction ________________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523426, 'iat': 1622523366, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 55, 6, 503610, tzinfo=datetime.timezone.utc) expired_ts = 1622523306 jwt_perms = now_ts = 1622523366 otk = p = plus1min_ts = 1622523426 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjYsImV4cCI6MTYyMjUyMzMwNn0.hHYD9aUeBiFeIo09T5XD_c2b8vath1P3xpntzNtj4HU' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjYsImV4cCI6MTYyMjUyMzMwNn0.hHYD9aUeBiFeIo09T5XD_c2b8vath1P3xpntzNtj4HU' roundup/anypy/strings.py:21: AttributeError _________________________ sqliteRestTest.testPatchAdd __________________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523426, 'iat': 1622523366, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 55, 6, 737997, tzinfo=datetime.timezone.utc) expired_ts = 1622523306 jwt_perms = now_ts = 1622523366 otk = p = plus1min_ts = 1622523426 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjYsImV4cCI6MTYyMjUyMzMwNn0.hHYD9aUeBiFeIo09T5XD_c2b8vath1P3xpntzNtj4HU' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjYsImV4cCI6MTYyMjUyMzMwNn0.hHYD9aUeBiFeIo09T5XD_c2b8vath1P3xpntzNtj4HU' roundup/anypy/strings.py:21: AttributeError ________________________ sqliteRestTest.testPatchRemove ________________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523426, 'iat': 1622523366, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 55, 6, 947818, tzinfo=datetime.timezone.utc) expired_ts = 1622523306 jwt_perms = now_ts = 1622523366 otk = p = plus1min_ts = 1622523426 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjYsImV4cCI6MTYyMjUyMzMwNn0.hHYD9aUeBiFeIo09T5XD_c2b8vath1P3xpntzNtj4HU' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjYsImV4cCI6MTYyMjUyMzMwNn0.hHYD9aUeBiFeIo09T5XD_c2b8vath1P3xpntzNtj4HU' roundup/anypy/strings.py:21: AttributeError ______________________ sqliteRestTest.testPatchRemoveAll _______________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523427, 'iat': 1622523367, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 55, 7, 267321, tzinfo=datetime.timezone.utc) expired_ts = 1622523307 jwt_perms = now_ts = 1622523367 otk = p = plus1min_ts = 1622523427 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjcsImV4cCI6MTYyMjUyMzMwN30.JgcoPlHs2ybCN4GR0YzoibMPaeJKp5SQhHSxT31JWqY' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjcsImV4cCI6MTYyMjUyMzMwN30.JgcoPlHs2ybCN4GR0YzoibMPaeJKp5SQhHSxT31JWqY' roundup/anypy/strings.py:21: AttributeError _______________________ sqliteRestTest.testPatchReplace ________________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523427, 'iat': 1622523367, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 55, 7, 468172, tzinfo=datetime.timezone.utc) expired_ts = 1622523307 jwt_perms = now_ts = 1622523367 otk = p = plus1min_ts = 1622523427 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjcsImV4cCI6MTYyMjUyMzMwN30.JgcoPlHs2ybCN4GR0YzoibMPaeJKp5SQhHSxT31JWqY' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjcsImV4cCI6MTYyMjUyMzMwN30.JgcoPlHs2ybCN4GR0YzoibMPaeJKp5SQhHSxT31JWqY' roundup/anypy/strings.py:21: AttributeError ___________________________ sqliteRestTest.testPost ____________________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523427, 'iat': 1622523367, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 55, 7, 755748, tzinfo=datetime.timezone.utc) expired_ts = 1622523307 jwt_perms = now_ts = 1622523367 otk = p = plus1min_ts = 1622523427 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjcsImV4cCI6MTYyMjUyMzMwN30.JgcoPlHs2ybCN4GR0YzoibMPaeJKp5SQhHSxT31JWqY' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjcsImV4cCI6MTYyMjUyMzMwN30.JgcoPlHs2ybCN4GR0YzoibMPaeJKp5SQhHSxT31JWqY' roundup/anypy/strings.py:21: AttributeError _________________________ sqliteRestTest.testPostFile __________________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523427, 'iat': 1622523367, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 55, 7, 950079, tzinfo=datetime.timezone.utc) expired_ts = 1622523307 jwt_perms = now_ts = 1622523367 otk = p = plus1min_ts = 1622523427 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjcsImV4cCI6MTYyMjUyMzMwN30.JgcoPlHs2ybCN4GR0YzoibMPaeJKp5SQhHSxT31JWqY' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjcsImV4cCI6MTYyMjUyMzMwN30.JgcoPlHs2ybCN4GR0YzoibMPaeJKp5SQhHSxT31JWqY' roundup/anypy/strings.py:21: AttributeError __________________________ sqliteRestTest.testPostPOE __________________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523428, 'iat': 1622523368, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 55, 8, 153574, tzinfo=datetime.timezone.utc) expired_ts = 1622523308 jwt_perms = now_ts = 1622523368 otk = p = plus1min_ts = 1622523428 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjgsImV4cCI6MTYyMjUyMzMwOH0.GDGBIaam8vxH2PIci3G1YOetopyyREA4T5y8xrdv_aQ' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjgsImV4cCI6MTYyMjUyMzMwOH0.GDGBIaam8vxH2PIci3G1YOetopyyREA4T5y8xrdv_aQ' roundup/anypy/strings.py:21: AttributeError _______________________ sqliteRestTest.testPutAttribute ________________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523428, 'iat': 1622523368, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 55, 8, 449858, tzinfo=datetime.timezone.utc) expired_ts = 1622523308 jwt_perms = now_ts = 1622523368 otk = p = plus1min_ts = 1622523428 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjgsImV4cCI6MTYyMjUyMzMwOH0.GDGBIaam8vxH2PIci3G1YOetopyyREA4T5y8xrdv_aQ' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjgsImV4cCI6MTYyMjUyMzMwOH0.GDGBIaam8vxH2PIci3G1YOetopyyREA4T5y8xrdv_aQ' roundup/anypy/strings.py:21: AttributeError ________________________ sqliteRestTest.testPutElement _________________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523428, 'iat': 1622523368, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 55, 8, 709077, tzinfo=datetime.timezone.utc) expired_ts = 1622523308 jwt_perms = now_ts = 1622523368 otk = p = plus1min_ts = 1622523428 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjgsImV4cCI6MTYyMjUyMzMwOH0.GDGBIaam8vxH2PIci3G1YOetopyyREA4T5y8xrdv_aQ' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjgsImV4cCI6MTYyMjUyMzMwOH0.GDGBIaam8vxH2PIci3G1YOetopyyREA4T5y8xrdv_aQ' roundup/anypy/strings.py:21: AttributeError _______________________ sqliteRestTest.testRestRateLimit _______________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523429, 'iat': 1622523369, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 55, 9, 107223, tzinfo=datetime.timezone.utc) expired_ts = 1622523309 jwt_perms = now_ts = 1622523369 otk = p = plus1min_ts = 1622523429 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjksImV4cCI6MTYyMjUyMzMwOX0.sNXcFgfLlCVCko4ArL619BFIMo5jOg4kPgExTgPVRrE' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjksImV4cCI6MTYyMjUyMzMwOX0.sNXcFgfLlCVCko4ArL619BFIMo5jOg4kPgExTgPVRrE' roundup/anypy/strings.py:21: AttributeError __________________________ sqliteRestTest.testSorting __________________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523429, 'iat': 1622523369, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 55, 9, 314095, tzinfo=datetime.timezone.utc) expired_ts = 1622523309 jwt_perms = now_ts = 1622523369 otk = p = plus1min_ts = 1622523429 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjksImV4cCI6MTYyMjUyMzMwOX0.sNXcFgfLlCVCko4ArL619BFIMo5jOg4kPgExTgPVRrE' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjksImV4cCI6MTYyMjUyMzMwOX0.sNXcFgfLlCVCko4ArL619BFIMo5jOg4kPgExTgPVRrE' roundup/anypy/strings.py:21: AttributeError _________________________ sqliteRestTest.testStatsGen __________________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523429, 'iat': 1622523369, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 55, 9, 552203, tzinfo=datetime.timezone.utc) expired_ts = 1622523309 jwt_perms = now_ts = 1622523369 otk = p = plus1min_ts = 1622523429 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjksImV4cCI6MTYyMjUyMzMwOX0.sNXcFgfLlCVCko4ArL619BFIMo5jOg4kPgExTgPVRrE' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjksImV4cCI6MTYyMjUyMzMwOX0.sNXcFgfLlCVCko4ArL619BFIMo5jOg4kPgExTgPVRrE' roundup/anypy/strings.py:21: AttributeError ______________________ sqliteRestTest.testTransitiveField ______________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523429, 'iat': 1622523369, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 55, 9, 852699, tzinfo=datetime.timezone.utc) expired_ts = 1622523309 jwt_perms = now_ts = 1622523369 otk = p = plus1min_ts = 1622523429 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjksImV4cCI6MTYyMjUyMzMwOX0.sNXcFgfLlCVCko4ArL619BFIMo5jOg4kPgExTgPVRrE' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNjksImV4cCI6MTYyMjUyMzMwOX0.sNXcFgfLlCVCko4ArL619BFIMo5jOg4kPgExTgPVRrE' roundup/anypy/strings.py:21: AttributeError _____________________ sqliteRestTest.test_bad_audience_jwt _____________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523430, 'iat': 1622523370, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 55, 10, 67556, tzinfo=datetime.timezone.utc) expired_ts = 1622523310 jwt_perms = now_ts = 1622523370 otk = p = plus1min_ts = 1622523430 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNzAsImV4cCI6MTYyMjUyMzMxMH0.1ZdWlmqv7Q3yvaRb77_ci8QbW2cTja0PuRf9p5z1hcg' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNzAsImV4cCI6MTYyMjUyMzMxMH0.1ZdWlmqv7Q3yvaRb77_ci8QbW2cTja0PuRf9p5z1hcg' roundup/anypy/strings.py:21: AttributeError ______________________ sqliteRestTest.test_bad_issue_jwt _______________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523430, 'iat': 1622523370, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 55, 10, 381446, tzinfo=datetime.timezone.utc) expired_ts = 1622523310 jwt_perms = now_ts = 1622523370 otk = p = plus1min_ts = 1622523430 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNzAsImV4cCI6MTYyMjUyMzMxMH0.1ZdWlmqv7Q3yvaRb77_ci8QbW2cTja0PuRf9p5z1hcg' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNzAsImV4cCI6MTYyMjUyMzMxMH0.1ZdWlmqv7Q3yvaRb77_ci8QbW2cTja0PuRf9p5z1hcg' roundup/anypy/strings.py:21: AttributeError ______________________ sqliteRestTest.test_bad_roles_jwt _______________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523430, 'iat': 1622523370, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 55, 10, 651721, tzinfo=datetime.timezone.utc) expired_ts = 1622523310 jwt_perms = now_ts = 1622523370 otk = p = plus1min_ts = 1622523430 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNzAsImV4cCI6MTYyMjUyMzMxMH0.1ZdWlmqv7Q3yvaRb77_ci8QbW2cTja0PuRf9p5z1hcg' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNzAsImV4cCI6MTYyMjUyMzMxMH0.1ZdWlmqv7Q3yvaRb77_ci8QbW2cTja0PuRf9p5z1hcg' roundup/anypy/strings.py:21: AttributeError _____________________ sqliteRestTest.test_bad_subject_jwt ______________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523430, 'iat': 1622523370, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 55, 10, 995313, tzinfo=datetime.timezone.utc) expired_ts = 1622523310 jwt_perms = now_ts = 1622523370 otk = p = plus1min_ts = 1622523430 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNzAsImV4cCI6MTYyMjUyMzMxMH0.1ZdWlmqv7Q3yvaRb77_ci8QbW2cTja0PuRf9p5z1hcg' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNzAsImV4cCI6MTYyMjUyMzMxMH0.1ZdWlmqv7Q3yvaRb77_ci8QbW2cTja0PuRf9p5z1hcg' roundup/anypy/strings.py:21: AttributeError _______________________ sqliteRestTest.test_disabled_jwt _______________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523431, 'iat': 1622523371, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 55, 11, 186862, tzinfo=datetime.timezone.utc) expired_ts = 1622523311 jwt_perms = now_ts = 1622523371 otk = p = plus1min_ts = 1622523431 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNzEsImV4cCI6MTYyMjUyMzMxMX0.GHGHPWCWZSeezA54JlkNSKKjvbHZS1Zc8OIwt1xf_hw' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNzEsImV4cCI6MTYyMjUyMzMxMX0.GHGHPWCWZSeezA54JlkNSKKjvbHZS1Zc8OIwt1xf_hw' roundup/anypy/strings.py:21: AttributeError _______________________ sqliteRestTest.test_expired_jwt ________________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523431, 'iat': 1622523371, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 55, 11, 417770, tzinfo=datetime.timezone.utc) expired_ts = 1622523311 jwt_perms = now_ts = 1622523371 otk = p = plus1min_ts = 1622523431 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNzEsImV4cCI6MTYyMjUyMzMxMX0.GHGHPWCWZSeezA54JlkNSKKjvbHZS1Zc8OIwt1xf_hw' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNzEsImV4cCI6MTYyMjUyMzMxMX0.GHGHPWCWZSeezA54JlkNSKKjvbHZS1Zc8OIwt1xf_hw' roundup/anypy/strings.py:21: AttributeError ______________________ sqliteRestTest.test_user_email_jwt ______________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523431, 'iat': 1622523371, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 55, 11, 776995, tzinfo=datetime.timezone.utc) expired_ts = 1622523311 jwt_perms = now_ts = 1622523371 otk = p = plus1min_ts = 1622523431 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNzEsImV4cCI6MTYyMjUyMzMxMX0.GHGHPWCWZSeezA54JlkNSKKjvbHZS1Zc8OIwt1xf_hw' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNzEsImV4cCI6MTYyMjUyMzMxMX0.GHGHPWCWZSeezA54JlkNSKKjvbHZS1Zc8OIwt1xf_hw' roundup/anypy/strings.py:21: AttributeError ___________________ sqliteRestTest.test_user_emailnorest_jwt ___________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523431, 'iat': 1622523371, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 55, 11, 986972, tzinfo=datetime.timezone.utc) expired_ts = 1622523311 jwt_perms = now_ts = 1622523371 otk = p = plus1min_ts = 1622523431 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNzEsImV4cCI6MTYyMjUyMzMxMX0.GHGHPWCWZSeezA54JlkNSKKjvbHZS1Zc8OIwt1xf_hw' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNzEsImV4cCI6MTYyMjUyMzMxMX0.GHGHPWCWZSeezA54JlkNSKKjvbHZS1Zc8OIwt1xf_hw' roundup/anypy/strings.py:21: AttributeError _________________________ sqliteRestTest.test_user_jwt _________________________ self = def setUp(self): self.dirname = '_test_rest' # set up and open a tracker # Set optimize=True as code under test (Client.main()::determine_user) # will close and re-open the database on user changes. This wipes # out additions to the schema needed for testing. self.instance = setupTracker(self.dirname, self.backend, optimize=True) # open the database self.db = self.instance.open('admin') # Create the Otk db. # This allows a test later on to open the existing db and # set a class attribute to test the open retry loop # just as though this process was using a pre-existing db # rather then the new one we create. otk = OneTimeKeys(self.db) otk.set('key', key="value") # Get user id (user4 maybe). Used later to get data from db. self.joeid = self.db.user.create( username='joe', password=password.Password('random'), address='random@home.org', realname='Joe Random', roles='User' ) self.db.user.set('1', address="admin@admin.com") self.db.user.set('2', address="anon@admin.com") self.db.commit() self.db.close() self.db = self.instance.open('joe') # Allow joe to retire p = self.db.security.addPermission(name='Retire', klass='issue') self.db.security.addPermissionToRole('User', p) # add set of roles for testing jwt's. self.db.security.addRole(name="User:email", description="allow email by jwt") # allow the jwt to access everybody's email addresses. # this makes it easier to differentiate between User and # User:email roles by accessing the /rest/data/user # endpoint jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email", props_only=False) self.db.security.addPermissionToRole("User:email", jwt_perms) self.db.security.addPermissionToRole("User:email", "Rest Access") # add set of roles for testing jwt's. # this is like the user:email role, but it missing access to the rest endpoint. self.db.security.addRole(name="User:emailnorest", description="allow email by jwt") jwt_perms = self.db.security.addPermission(name='View', klass='user', properties=('id', 'realname', 'address', 'username'), description="Allow jwt access to email but forget to allow rest", props_only=False) self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) if jwt: # must be 32 chars in length minimum (I think this is at least # 256 bits of data) secret = "TestingTheJwtSecretTestingTheJwtSecret" self.db.config['WEB_JWT_SECRET'] = secret # generate all timestamps in UTC. base_datetime = datetime(1970,1,1, tzinfo=myutc) # A UTC timestamp for now. dt = datetime.now(myutc) now_ts = int((dt - base_datetime).total_seconds()) # one good for a minute dt = dt + timedelta(seconds=60) plus1min_ts = int((dt - base_datetime).total_seconds()) # one that expired a minute ago dt = dt - timedelta(seconds=120) expired_ts = int((dt - base_datetime).total_seconds()) # claims match what cgi/client.py::determine_user # is looking for claim= { 'sub': self.db.getuid(), 'iss': self.db.config.TRACKER_WEB, 'aud': self.db.config.TRACKER_WEB, 'roles': [ 'User' ], 'iat': now_ts, 'exp': plus1min_ts, } self.jwt = {} self.claim = {} # generate invalid claim with expired timestamp self.claim['expired'] = copy(claim) self.claim['expired']['exp'] = expired_ts > self.jwt['expired'] = b2s(jwt.encode(self.claim['expired'], secret, algorithm='HS256')) base_datetime = datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) claim = {'aud': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'exp': 1622523432, 'iat': 1622523372, 'iss': 'http://tracker.example/cgi-bin/roundup.cgi/bugs/', 'roles': ['User'], 'sub': '3'} dt = datetime.datetime(2021, 6, 1, 4, 55, 12, 287374, tzinfo=datetime.timezone.utc) expired_ts = 1622523312 jwt_perms = now_ts = 1622523372 otk = p = plus1min_ts = 1622523432 secret = 'TestingTheJwtSecretTestingTheJwtSecret' self = test/rest_common.py:171: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dp...3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNzIsImV4cCI6MTYyMjUyMzMxMn0.7qRbmTIGG3ML7FBgCUA-F28jJie6OtgSNlpzTdaSq1k' def b2s(b): """Convert a UTF-8 encoded bytes object to the internal string format.""" if _py3: > return b.decode('utf-8') E AttributeError: 'str' object has no attribute 'decode' b = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIzIiwiaXNzIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwiYXVkIjoiaHR0cDovL3RyYWNrZXIuZXhhbXBsZS9jZ2ktYmluL3JvdW5kdXAuY2dpL2J1Z3MvIiwicm9sZXMiOlsiVXNlciJdLCJpYXQiOjE2MjI1MjMzNzIsImV4cCI6MTYyMjUyMzMxMn0.7qRbmTIGG3ML7FBgCUA-F28jJie6OtgSNlpzTdaSq1k' roundup/anypy/strings.py:21: AttributeError =============================== warnings summary =============================== roundup/configuration.py:12 /var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0/roundup/configuration.py:12: DeprecationWarning: the imp module is deprecated in favour of importlib; see the module's documentation for alternative uses import imp -- Docs: https://docs.pytest.org/en/stable/warnings.html =========================== short test summary info ============================ SKIPPED [1] test/test_admin.py:368: Skipping MySQL tests: backend not available SKIPPED [1] test/test_admin.py:442: Skipping MySQL tests: backend not available SKIPPED [1] test/test_admin.py:232: Skipping MySQL tests: backend not available SKIPPED [1] test/test_admin.py:303: Skipping MySQL tests: backend not available SKIPPED [1] test/test_admin.py:98: Skipping MySQL tests: backend not available SKIPPED [1] test/test_admin.py:190: Skipping MySQL tests: backend not available SKIPPED [1] test/test_admin.py:200: Skipping MySQL tests: backend not available SKIPPED [1] test/test_admin.py:580: Skipping MySQL tests: backend not available SKIPPED [1] test/test_admin.py:687: Skipping MySQL tests: backend not available SKIPPED [1] test/test_admin.py:760: Skipping MySQL tests: backend not available SKIPPED [1] test/test_admin.py:790: Skipping MySQL tests: backend not available SKIPPED [1] test/test_admin.py:368: Skipping PostgreSQL tests: database not available SKIPPED [1] test/test_admin.py:442: Skipping PostgreSQL tests: database not available SKIPPED [1] test/test_admin.py:232: Skipping PostgreSQL tests: database not available SKIPPED [1] test/test_admin.py:303: Skipping PostgreSQL tests: database not available SKIPPED [1] test/test_admin.py:98: Skipping PostgreSQL tests: database not available SKIPPED [1] test/test_admin.py:190: Skipping PostgreSQL tests: database not available SKIPPED [1] test/test_admin.py:200: Skipping PostgreSQL tests: database not available SKIPPED [1] test/test_admin.py:580: Skipping PostgreSQL tests: database not available SKIPPED [1] test/test_admin.py:687: Skipping PostgreSQL tests: database not available SKIPPED [1] test/test_admin.py:760: Skipping PostgreSQL tests: database not available SKIPPED [1] test/test_admin.py:790: Skipping PostgreSQL tests: database not available SKIPPED [3] test/db_test_base.py:2856: Skipping PGPNosy test SKIPPED [1] test/test_indexer.py:80: Skipping Whoosh indexer tests: 'whoosh' not installed SKIPPED [1] test/test_indexer.py:135: Skipping Whoosh indexer tests: 'whoosh' not installed SKIPPED [1] test/test_indexer.py:88: Skipping Whoosh indexer tests: 'whoosh' not installed SKIPPED [1] test/test_indexer.py:96: Skipping Whoosh indexer tests: 'whoosh' not installed SKIPPED [1] test/test_indexer.py:115: Skipping Whoosh indexer tests: 'whoosh' not installed SKIPPED [1] test/test_indexer.py:154: Skipping Whoosh indexer tests: 'whoosh' not installed SKIPPED [1] test/test_indexer.py:104: Skipping Whoosh indexer tests: 'whoosh' not installed SKIPPED [1] test/test_indexer.py:160: Skipping Whoosh indexer tests: 'whoosh' not installed SKIPPED [1] test/test_indexer.py:146: Skipping Whoosh indexer tests: 'whoosh' not installed SKIPPED [1] test/test_indexer.py:80: Skipping Xapian indexer tests: 'xapian' not installed SKIPPED [1] test/test_indexer.py:135: Skipping Xapian indexer tests: 'xapian' not installed SKIPPED [1] test/test_indexer.py:88: Skipping Xapian indexer tests: 'xapian' not installed SKIPPED [1] test/test_indexer.py:96: Skipping Xapian indexer tests: 'xapian' not installed SKIPPED [1] test/test_indexer.py:115: Skipping Xapian indexer tests: 'xapian' not installed SKIPPED [1] test/test_indexer.py:154: Skipping Xapian indexer tests: 'xapian' not installed SKIPPED [1] test/test_indexer.py:104: Skipping Xapian indexer tests: 'xapian' not installed SKIPPED [1] test/test_indexer.py:160: Skipping Xapian indexer tests: 'xapian' not installed SKIPPED [1] test/test_indexer.py:146: Skipping Xapian indexer tests: 'xapian' not installed SKIPPED [1] test/test_indexer.py:80: Skipping PostgreSQL tests: database not available SKIPPED [1] test/test_indexer.py:135: Skipping PostgreSQL tests: database not available SKIPPED [1] test/test_indexer.py:88: Skipping PostgreSQL tests: database not available SKIPPED [1] test/test_indexer.py:96: Skipping PostgreSQL tests: database not available SKIPPED [1] test/test_indexer.py:115: Skipping PostgreSQL tests: database not available SKIPPED [1] test/test_indexer.py:154: Skipping PostgreSQL tests: database not available SKIPPED [1] test/test_indexer.py:104: Skipping PostgreSQL tests: database not available SKIPPED [1] test/test_indexer.py:160: Skipping PostgreSQL tests: database not available SKIPPED [1] test/test_indexer.py:146: Skipping PostgreSQL tests: database not available SKIPPED [1] test/test_indexer.py:80: Skipping MySQL tests: backend not available SKIPPED [1] test/test_indexer.py:135: Skipping MySQL tests: backend not available SKIPPED [1] test/test_indexer.py:88: Skipping MySQL tests: backend not available SKIPPED [1] test/test_indexer.py:96: Skipping MySQL tests: backend not available SKIPPED [1] test/test_indexer.py:115: Skipping MySQL tests: backend not available SKIPPED [1] test/test_indexer.py:154: Skipping MySQL tests: backend not available SKIPPED [1] test/test_indexer.py:104: Skipping MySQL tests: backend not available SKIPPED [1] test/test_indexer.py:160: Skipping MySQL tests: backend not available SKIPPED [1] test/test_indexer.py:146: Skipping MySQL tests: backend not available SKIPPED [1] test/test_mailgw.py:281: Skipping PGP tests: 'gpg' not installed SKIPPED [1] test/test_mailgw.py:261: Skipping PGP tests: 'gpg' not installed SKIPPED [1] test/test_mailgw.py:4648: Skipping PGP tests: 'gpg' not installed SKIPPED [1] test/test_mailgw.py:4592: Skipping PGP tests: 'gpg' not installed SKIPPED [1] test/test_mailgw.py:4618: Skipping PGP tests: 'gpg' not installed SKIPPED [1] test/test_mailgw.py:4589: Skipping PGP tests: 'gpg' not installed SKIPPED [1] test/test_mailgw.py:4603: Skipping PGP tests: 'gpg' not installed SKIPPED [1] test/test_mailgw.py:4539: Skipping PGP tests: 'gpg' not installed SKIPPED [1] test/test_mailgw.py:4549: Skipping PGP tests: 'gpg' not installed SKIPPED [1] test/test_mailgw.py:4495: Skipping PGP tests: 'gpg' not installed SKIPPED [1] test/db_test_base.py:239: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:2749: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:2776: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:2723: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:2550: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:2602: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:954: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:934: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:565: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:559: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:573: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:729: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:231: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:444: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:467: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:494: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:506: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:484: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:969: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:785: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:797: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:794: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:598: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:619: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:260: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:843: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:287: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:1504: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:1485: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:1717: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:2002: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:2134: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:2146: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:1710: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:2044: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:1835: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:2116: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:1887: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:1960: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:1910: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:1895: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:2085: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:2099: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:1745: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:1966: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:2022: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:2014: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:1989: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:1974: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:1980: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:1904: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:2405: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:1847: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:1918: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:2399: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:1819: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:1756: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:1769: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:1786: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:2056: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:2281: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:2205: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:2258: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:2165: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:2186: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:2297: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:2332: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:1543: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:1558: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:1653: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:1594: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:1599: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:1635: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:1623: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:1644: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:1649: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:1608: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:1662: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:1567: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:1515: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:247: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:251: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:2439: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:1441: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:1407: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:1432: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:1527: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:626: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:639: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:529: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:548: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:1321: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:1379: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:1263: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:664: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:683: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:342: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:353: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:386: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:362: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:403: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:2790: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:2825: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:579: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:592: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:2856: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:1384: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:645: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:657: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:1009: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:1034: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:984: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:225: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:1455: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:1470: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:2763: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:695: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:737: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:315: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:265: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:1668: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:298: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:276: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:748: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:1031: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:2935: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:2985: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:3066: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:2969: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:3108: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:3024: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:3150: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:3087: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:3131: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:2952: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:3315: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:3344: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:3405: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:3446: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:3425: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:3177: Skipping MySQL tests: backend not available SKIPPED [1] test/session_common.py:32: Skipping MySQL tests: backend not available SKIPPED [1] test/session_common.py:27: Skipping MySQL tests: backend not available SKIPPED [1] test/session_common.py:22: Skipping MySQL tests: backend not available SKIPPED [1] test/session_common.py:39: Skipping MySQL tests: backend not available SKIPPED [1] test/session_common.py:44: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:3633: Skipping MySQL tests: backend not available SKIPPED [1] test/rest_common.py:1926: Skipping MySQL tests: backend not available SKIPPED [1] test/rest_common.py:2566: Skipping MySQL tests: backend not available SKIPPED [1] test/rest_common.py:2550: Skipping MySQL tests: backend not available SKIPPED [1] test/rest_common.py:2538: Skipping MySQL tests: backend not available SKIPPED [1] test/rest_common.py:2525: Skipping MySQL tests: backend not available SKIPPED [1] test/rest_common.py:1220: Skipping MySQL tests: backend not available SKIPPED [1] test/rest_common.py:2582: Skipping MySQL tests: backend not available SKIPPED [1] test/rest_common.py:1423: Skipping MySQL tests: backend not available SKIPPED [1] test/rest_common.py:1261: Skipping MySQL tests: backend not available SKIPPED [1] test/rest_common.py:1108: Skipping MySQL tests: backend not available SKIPPED [1] test/rest_common.py:1161: Skipping MySQL tests: backend not available SKIPPED [1] test/rest_common.py:692: Skipping MySQL tests: backend not available SKIPPED [1] test/rest_common.py:302: Skipping MySQL tests: backend not available SKIPPED [1] test/rest_common.py:385: Skipping MySQL tests: backend not available SKIPPED [1] test/rest_common.py:347: Skipping MySQL tests: backend not available SKIPPED [1] test/rest_common.py:2034: Skipping MySQL tests: backend not available SKIPPED [1] test/rest_common.py:413: Skipping MySQL tests: backend not available SKIPPED [1] test/rest_common.py:873: Skipping MySQL tests: backend not available SKIPPED [1] test/rest_common.py:2974: Skipping MySQL tests: backend not available SKIPPED [1] test/rest_common.py:2686: Skipping MySQL tests: backend not available SKIPPED [1] test/rest_common.py:3022: Skipping MySQL tests: backend not available SKIPPED [1] test/rest_common.py:2887: Skipping MySQL tests: backend not available SKIPPED [1] test/rest_common.py:2781: Skipping MySQL tests: backend not available SKIPPED [1] test/rest_common.py:2480: Skipping MySQL tests: backend not available SKIPPED [1] test/rest_common.py:2497: Skipping MySQL tests: backend not available SKIPPED [1] test/rest_common.py:2097: Skipping MySQL tests: backend not available SKIPPED [1] test/rest_common.py:2433: Skipping MySQL tests: backend not available SKIPPED [1] test/rest_common.py:2344: Skipping MySQL tests: backend not available SKIPPED [1] test/rest_common.py:992: Skipping MySQL tests: backend not available SKIPPED [1] test/rest_common.py:642: Skipping MySQL tests: backend not available SKIPPED [1] test/rest_common.py:1322: Skipping MySQL tests: backend not available SKIPPED [1] test/rest_common.py:670: Skipping MySQL tests: backend not available SKIPPED [1] test/rest_common.py:3421: Skipping MySQL tests: backend not available SKIPPED [1] test/rest_common.py:3385: Skipping MySQL tests: backend not available SKIPPED [1] test/rest_common.py:3457: Skipping MySQL tests: backend not available SKIPPED [1] test/rest_common.py:3493: Skipping MySQL tests: backend not available SKIPPED [1] test/rest_common.py:3346: Skipping MySQL tests: backend not available SKIPPED [1] test/rest_common.py:3082: Skipping MySQL tests: backend not available SKIPPED [1] test/rest_common.py:3214: Skipping MySQL tests: backend not available SKIPPED [1] test/rest_common.py:3282: Skipping MySQL tests: backend not available SKIPPED [1] test/rest_common.py:3144: Skipping MySQL tests: backend not available SKIPPED [1] test/db_test_base.py:239: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:2749: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:2776: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:2723: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:2550: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:2602: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:954: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:934: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:565: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:559: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:573: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:729: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:231: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:444: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:467: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:494: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:506: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:484: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:969: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:785: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:797: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:794: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:598: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:619: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:260: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:843: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:287: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:1504: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:1485: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:1717: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:2002: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:2134: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:2146: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:1710: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:2044: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:1835: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:2116: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:1887: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:1960: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:1910: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:1895: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:2085: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:2099: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:1745: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:1966: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:2022: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:2014: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:1989: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:1974: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:1980: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:1904: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:2405: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:1847: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:1918: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:2399: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:1819: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:1756: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:1769: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:1786: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:2056: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:2281: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:2205: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:2258: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:2165: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:2186: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:2297: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:2332: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:1543: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:1558: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:1653: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:1594: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:1599: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:1635: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:1623: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:1644: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:1649: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:1608: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:1662: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:1567: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:1515: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:247: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:251: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:2439: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:1441: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:1407: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:1432: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:1527: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:626: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:639: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:529: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:548: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:1321: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:1379: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:1263: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:664: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:683: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:342: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:353: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:386: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:362: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:403: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:2790: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:2825: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:579: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:592: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:2856: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:1384: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:645: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:657: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:1009: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:1034: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:984: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:225: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:1455: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:1470: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:2763: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:695: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:737: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:315: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:265: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:1668: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:298: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:276: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:748: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:1031: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:2935: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:3344: Skipping PostgreSQL tests: database not available SKIPPED [1] test/test_postgresql.py:145: Skipping PostgreSQL tests: database not available SKIPPED [1] test/test_postgresql.py:151: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:3405: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:3446: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:3425: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:3177: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:2985: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:3066: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:2969: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:3108: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:3024: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:3150: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:3087: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:3131: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:2952: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:3315: Skipping PostgreSQL tests: database not available SKIPPED [1] test/session_common.py:32: Skipping PostgreSQL tests: database not available SKIPPED [1] test/session_common.py:27: Skipping PostgreSQL tests: database not available SKIPPED [1] test/session_common.py:22: Skipping PostgreSQL tests: database not available SKIPPED [1] test/session_common.py:39: Skipping PostgreSQL tests: database not available SKIPPED [1] test/session_common.py:44: Skipping PostgreSQL tests: database not available SKIPPED [1] test/db_test_base.py:3633: Skipping PostgreSQL tests: database not available SKIPPED [1] test/rest_common.py:1926: Skipping PostgreSQL tests: database not available SKIPPED [1] test/rest_common.py:2566: Skipping PostgreSQL tests: database not available SKIPPED [1] test/rest_common.py:2550: Skipping PostgreSQL tests: database not available SKIPPED [1] test/rest_common.py:2538: Skipping PostgreSQL tests: database not available SKIPPED [1] test/rest_common.py:2525: Skipping PostgreSQL tests: database not available SKIPPED [1] test/rest_common.py:1220: Skipping PostgreSQL tests: database not available SKIPPED [1] test/rest_common.py:2582: Skipping PostgreSQL tests: database not available SKIPPED [1] test/rest_common.py:1423: Skipping PostgreSQL tests: database not available SKIPPED [1] test/rest_common.py:1261: Skipping PostgreSQL tests: database not available SKIPPED [1] test/rest_common.py:1108: Skipping PostgreSQL tests: database not available SKIPPED [1] test/rest_common.py:1161: Skipping PostgreSQL tests: database not available SKIPPED [1] test/rest_common.py:692: Skipping PostgreSQL tests: database not available SKIPPED [1] test/rest_common.py:302: Skipping PostgreSQL tests: database not available SKIPPED [1] test/rest_common.py:385: Skipping PostgreSQL tests: database not available SKIPPED [1] test/rest_common.py:347: Skipping PostgreSQL tests: database not available SKIPPED [1] test/rest_common.py:2034: Skipping PostgreSQL tests: database not available SKIPPED [1] test/rest_common.py:413: Skipping PostgreSQL tests: database not available SKIPPED [1] test/rest_common.py:873: Skipping PostgreSQL tests: database not available SKIPPED [1] test/rest_common.py:2974: Skipping PostgreSQL tests: database not available SKIPPED [1] test/rest_common.py:2686: Skipping PostgreSQL tests: database not available SKIPPED [1] test/rest_common.py:3022: Skipping PostgreSQL tests: database not available SKIPPED [1] test/rest_common.py:2887: Skipping PostgreSQL tests: database not available SKIPPED [1] test/rest_common.py:2781: Skipping PostgreSQL tests: database not available SKIPPED [1] test/rest_common.py:2480: Skipping PostgreSQL tests: database not available SKIPPED [1] test/rest_common.py:2497: Skipping PostgreSQL tests: database not available SKIPPED [1] test/rest_common.py:2097: Skipping PostgreSQL tests: database not available SKIPPED [1] test/rest_common.py:2433: Skipping PostgreSQL tests: database not available SKIPPED [1] test/rest_common.py:2344: Skipping PostgreSQL tests: database not available SKIPPED [1] test/rest_common.py:992: Skipping PostgreSQL tests: database not available SKIPPED [1] test/rest_common.py:642: Skipping PostgreSQL tests: database not available SKIPPED [1] test/rest_common.py:1322: Skipping PostgreSQL tests: database not available SKIPPED [1] test/rest_common.py:670: Skipping PostgreSQL tests: database not available SKIPPED [1] test/rest_common.py:3421: Skipping PostgreSQL tests: database not available SKIPPED [1] test/rest_common.py:3385: Skipping PostgreSQL tests: database not available SKIPPED [1] test/rest_common.py:3457: Skipping PostgreSQL tests: database not available SKIPPED [1] test/rest_common.py:3493: Skipping PostgreSQL tests: database not available SKIPPED [1] test/rest_common.py:3346: Skipping PostgreSQL tests: database not available SKIPPED [1] test/rest_common.py:3082: Skipping PostgreSQL tests: database not available SKIPPED [1] test/rest_common.py:3214: Skipping PostgreSQL tests: database not available SKIPPED [1] test/rest_common.py:3282: Skipping PostgreSQL tests: database not available SKIPPED [1] test/rest_common.py:3144: Skipping PostgreSQL tests: database not available SKIPPED [1] test/test_templating.py:293: StructuredText not available SKIPPED [1] test/test_templating.py:423: markdown2 not available SKIPPED [1] test/test_templating.py:450: markdown2 not available SKIPPED [1] test/test_templating.py:442: markdown2 not available SKIPPED [1] test/test_templating.py:431: markdown2 not available SKIPPED [1] test/test_xmlrpc.py:71: Skipping MySQL tests: backend not available SKIPPED [1] test/test_xmlrpc.py:155: Skipping MySQL tests: backend not available SKIPPED [1] test/test_xmlrpc.py:194: Skipping MySQL tests: backend not available SKIPPED [1] test/test_xmlrpc.py:184: Skipping MySQL tests: backend not available SKIPPED [1] test/test_xmlrpc.py:180: Skipping MySQL tests: backend not available SKIPPED [1] test/test_xmlrpc.py:175: Skipping MySQL tests: backend not available SKIPPED [1] test/test_xmlrpc.py:204: Skipping MySQL tests: backend not available SKIPPED [1] test/test_xmlrpc.py:81: Skipping MySQL tests: backend not available SKIPPED [1] test/test_xmlrpc.py:90: Skipping MySQL tests: backend not available SKIPPED [1] test/test_xmlrpc.py:97: Skipping MySQL tests: backend not available SKIPPED [1] test/test_xmlrpc.py:150: Skipping MySQL tests: backend not available SKIPPED [1] test/test_xmlrpc.py:297: Skipping MySQL tests: backend not available SKIPPED [1] test/test_xmlrpc.py:103: Skipping MySQL tests: backend not available SKIPPED [1] test/test_xmlrpc.py:71: Skipping PostgreSQL tests: database not available SKIPPED [1] test/test_xmlrpc.py:155: Skipping PostgreSQL tests: database not available SKIPPED [1] test/test_xmlrpc.py:194: Skipping PostgreSQL tests: database not available SKIPPED [1] test/test_xmlrpc.py:184: Skipping PostgreSQL tests: database not available SKIPPED [1] test/test_xmlrpc.py:180: Skipping PostgreSQL tests: database not available SKIPPED [1] test/test_xmlrpc.py:175: Skipping PostgreSQL tests: database not available SKIPPED [1] test/test_xmlrpc.py:204: Skipping PostgreSQL tests: database not available SKIPPED [1] test/test_xmlrpc.py:81: Skipping PostgreSQL tests: database not available SKIPPED [1] test/test_xmlrpc.py:90: Skipping PostgreSQL tests: database not available SKIPPED [1] test/test_xmlrpc.py:97: Skipping PostgreSQL tests: database not available SKIPPED [1] test/test_xmlrpc.py:150: Skipping PostgreSQL tests: database not available SKIPPED [1] test/test_xmlrpc.py:297: Skipping PostgreSQL tests: database not available SKIPPED [1] test/test_xmlrpc.py:103: Skipping PostgreSQL tests: database not available FAILED test/test_anydbm.py::anydbmRestTest::testAcceptHeaderParsing - Attribu... FAILED test/test_anydbm.py::anydbmRestTest::testAuthAllowedPost - AttributeEr... FAILED test/test_anydbm.py::anydbmRestTest::testAuthAllowedPut - AttributeErr... FAILED test/test_anydbm.py::anydbmRestTest::testAuthDeniedPost - AttributeErr... FAILED test/test_anydbm.py::anydbmRestTest::testAuthDeniedPut - AttributeErro... FAILED test/test_anydbm.py::anydbmRestTest::testBinaryFieldStorage - Attribut... FAILED test/test_anydbm.py::anydbmRestTest::testDeleteAttributeUri - Attribut... FAILED test/test_anydbm.py::anydbmRestTest::testDispatch - AttributeError: 's... FAILED test/test_anydbm.py::anydbmRestTest::testDispatchPost - AttributeError... FAILED test/test_anydbm.py::anydbmRestTest::testEtagGeneration - AttributeErr... FAILED test/test_anydbm.py::anydbmRestTest::testEtagProcessing - AttributeErr... FAILED test/test_anydbm.py::anydbmRestTest::testFilter - AttributeError: 'str... FAILED test/test_anydbm.py::anydbmRestTest::testGet - AttributeError: 'str' o... FAILED test/test_anydbm.py::anydbmRestTest::testGetExactMatch - AttributeErro... FAILED test/test_anydbm.py::anydbmRestTest::testGetTransitive - AttributeErro... FAILED test/test_anydbm.py::anydbmRestTest::testMethodOverride - AttributeErr... FAILED test/test_anydbm.py::anydbmRestTest::testOutputFormat - AttributeError... FAILED test/test_anydbm.py::anydbmRestTest::testPagination - AttributeError: ... FAILED test/test_anydbm.py::anydbmRestTest::testPatchAction - AttributeError:... FAILED test/test_anydbm.py::anydbmRestTest::testPatchAdd - AttributeError: 's... FAILED test/test_anydbm.py::anydbmRestTest::testPatchRemove - AttributeError:... FAILED test/test_anydbm.py::anydbmRestTest::testPatchRemoveAll - AttributeErr... FAILED test/test_anydbm.py::anydbmRestTest::testPatchReplace - AttributeError... FAILED test/test_anydbm.py::anydbmRestTest::testPost - AttributeError: 'str' ... FAILED test/test_anydbm.py::anydbmRestTest::testPostFile - AttributeError: 's... FAILED test/test_anydbm.py::anydbmRestTest::testPostPOE - AttributeError: 'st... FAILED test/test_anydbm.py::anydbmRestTest::testPutAttribute - AttributeError... FAILED test/test_anydbm.py::anydbmRestTest::testPutElement - AttributeError: ... FAILED test/test_anydbm.py::anydbmRestTest::testRestRateLimit - AttributeErro... FAILED test/test_anydbm.py::anydbmRestTest::testSorting - AttributeError: 'st... FAILED test/test_anydbm.py::anydbmRestTest::testStatsGen - AttributeError: 's... FAILED test/test_anydbm.py::anydbmRestTest::testTransitiveField - AttributeEr... FAILED test/test_anydbm.py::anydbmRestTest::test_bad_audience_jwt - Attribute... FAILED test/test_anydbm.py::anydbmRestTest::test_bad_issue_jwt - AttributeErr... FAILED test/test_anydbm.py::anydbmRestTest::test_bad_roles_jwt - AttributeErr... FAILED test/test_anydbm.py::anydbmRestTest::test_bad_subject_jwt - AttributeE... FAILED test/test_anydbm.py::anydbmRestTest::test_disabled_jwt - AttributeErro... FAILED test/test_anydbm.py::anydbmRestTest::test_expired_jwt - AttributeError... FAILED test/test_anydbm.py::anydbmRestTest::test_user_email_jwt - AttributeEr... FAILED test/test_anydbm.py::anydbmRestTest::test_user_emailnorest_jwt - Attri... FAILED test/test_anydbm.py::anydbmRestTest::test_user_jwt - AttributeError: '... FAILED test/test_sqlite.py::sqliteRestTest::testAcceptHeaderParsing - Attribu... FAILED test/test_sqlite.py::sqliteRestTest::testAuthAllowedPost - AttributeEr... FAILED test/test_sqlite.py::sqliteRestTest::testAuthAllowedPut - AttributeErr... FAILED test/test_sqlite.py::sqliteRestTest::testAuthDeniedPost - AttributeErr... FAILED test/test_sqlite.py::sqliteRestTest::testAuthDeniedPut - AttributeErro... FAILED test/test_sqlite.py::sqliteRestTest::testBinaryFieldStorage - Attribut... FAILED test/test_sqlite.py::sqliteRestTest::testDeleteAttributeUri - Attribut... FAILED test/test_sqlite.py::sqliteRestTest::testDispatch - AttributeError: 's... FAILED test/test_sqlite.py::sqliteRestTest::testDispatchPost - AttributeError... FAILED test/test_sqlite.py::sqliteRestTest::testEtagGeneration - AttributeErr... FAILED test/test_sqlite.py::sqliteRestTest::testEtagProcessing - AttributeErr... FAILED test/test_sqlite.py::sqliteRestTest::testFilter - AttributeError: 'str... FAILED test/test_sqlite.py::sqliteRestTest::testGet - AttributeError: 'str' o... FAILED test/test_sqlite.py::sqliteRestTest::testGetExactMatch - AttributeErro... FAILED test/test_sqlite.py::sqliteRestTest::testGetTransitive - AttributeErro... FAILED test/test_sqlite.py::sqliteRestTest::testMethodOverride - AttributeErr... FAILED test/test_sqlite.py::sqliteRestTest::testOutputFormat - AttributeError... FAILED test/test_sqlite.py::sqliteRestTest::testPagination - AttributeError: ... FAILED test/test_sqlite.py::sqliteRestTest::testPatchAction - AttributeError:... FAILED test/test_sqlite.py::sqliteRestTest::testPatchAdd - AttributeError: 's... FAILED test/test_sqlite.py::sqliteRestTest::testPatchRemove - AttributeError:... FAILED test/test_sqlite.py::sqliteRestTest::testPatchRemoveAll - AttributeErr... FAILED test/test_sqlite.py::sqliteRestTest::testPatchReplace - AttributeError... FAILED test/test_sqlite.py::sqliteRestTest::testPost - AttributeError: 'str' ... FAILED test/test_sqlite.py::sqliteRestTest::testPostFile - AttributeError: 's... FAILED test/test_sqlite.py::sqliteRestTest::testPostPOE - AttributeError: 'st... FAILED test/test_sqlite.py::sqliteRestTest::testPutAttribute - AttributeError... FAILED test/test_sqlite.py::sqliteRestTest::testPutElement - AttributeError: ... FAILED test/test_sqlite.py::sqliteRestTest::testRestRateLimit - AttributeErro... FAILED test/test_sqlite.py::sqliteRestTest::testSorting - AttributeError: 'st... FAILED test/test_sqlite.py::sqliteRestTest::testStatsGen - AttributeError: 's... FAILED test/test_sqlite.py::sqliteRestTest::testTransitiveField - AttributeEr... FAILED test/test_sqlite.py::sqliteRestTest::test_bad_audience_jwt - Attribute... FAILED test/test_sqlite.py::sqliteRestTest::test_bad_issue_jwt - AttributeErr... FAILED test/test_sqlite.py::sqliteRestTest::test_bad_roles_jwt - AttributeErr... FAILED test/test_sqlite.py::sqliteRestTest::test_bad_subject_jwt - AttributeE... FAILED test/test_sqlite.py::sqliteRestTest::test_disabled_jwt - AttributeErro... FAILED test/test_sqlite.py::sqliteRestTest::test_expired_jwt - AttributeError... FAILED test/test_sqlite.py::sqliteRestTest::test_user_email_jwt - AttributeEr... FAILED test/test_sqlite.py::sqliteRestTest::test_user_emailnorest_jwt - Attri... FAILED test/test_sqlite.py::sqliteRestTest::test_user_jwt - AttributeError: '... ====== 82 failed, 911 passed, 480 skipped, 1 warning in 128.70s (0:02:08) ====== * ERROR: www-apps/roundup-2.0.0::gentoo failed (test phase): * pytest failed with python3.8 * * Call stack: * ebuild.sh, line 127: Called src_test * environment, line 2840: Called distutils-r1_src_test * environment, line 1246: Called _distutils-r1_run_foreach_impl 'python_test' * environment, line 501: Called python_foreach_impl 'distutils-r1_run_phase' 'python_test' * environment, line 2511: Called multibuild_foreach_variant '_python_multibuild_wrapper' 'distutils-r1_run_phase' 'python_test' * environment, line 2038: Called _multibuild_run '_python_multibuild_wrapper' 'distutils-r1_run_phase' 'python_test' * environment, line 2036: Called _python_multibuild_wrapper 'distutils-r1_run_phase' 'python_test' * environment, line 803: Called distutils-r1_run_phase 'python_test' * environment, line 1185: Called python_test * environment, line 2800: Called distutils-r1_python_test * environment, line 1142: Called epytest * environment, line 1559: Called die * The specific snippet of code: * "${@}" || die -n "pytest failed with ${EPYTHON}"; * * If you need support, post the output of `emerge --info '=www-apps/roundup-2.0.0::gentoo'`, * the complete build log and the output of `emerge -pqv '=www-apps/roundup-2.0.0::gentoo'`. * The complete build log is located at '/var/log/portage/www-apps:roundup-2.0.0:20210601-045402.log'. * For convenience, a symlink to the build log is located at '/var/tmp/portage/www-apps/roundup-2.0.0/temp/build.log'. * The ebuild environment file is located at '/var/tmp/portage/www-apps/roundup-2.0.0/temp/environment'. * Working directory: '/var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0' * S: '/var/tmp/portage/www-apps/roundup-2.0.0/work/roundup-2.0.0'