Go to:
Gentoo Home
Documentation
Forums
Lists
Bugs
Planet
Store
Wiki
Get Gentoo!
Gentoo's Bugzilla – Attachment 713052 Details for
Bug 793431
[TEST] www-apps/roundup-2.0.0 - E AttributeError: str object has no attribute decode
Home
|
New
–
[Ex]
|
Browse
|
Search
|
Privacy Policy
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
www-apps:roundup-2.0.0:20210601-045402.log
www-apps:roundup-2.0.0:20210601-045402.log (text/plain), 711.33 KB, created by
Toralf Förster
on 2021-06-01 07:14:41 UTC
(
hide
)
Description:
www-apps:roundup-2.0.0:20210601-045402.log
Filename:
MIME Type:
Creator:
Toralf Förster
Created:
2021-06-01 07:14:41 UTC
Size:
711.33 KB
patch
obsolete
> * 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 = <test.test_anydbm.anydbmRestTest testMethod=testAcceptHeaderParsing> > > 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 = <Permission 0x7f39185eb880 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523287 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f3918641e50> >p = <Permission 0x7f39185ebc40 'Retire','issue',None,None,None> >plus1min_ts = 1622523347 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_anydbm.anydbmRestTest testMethod=testAcceptHeaderParsing> > >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 = <test.test_anydbm.anydbmRestTest testMethod=testAuthAllowedPost> > > 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 = <Permission 0x7f39186f1c70 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523287 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f391896e4f0> >p = <Permission 0x7f39186f1970 'Retire','issue',None,None,None> >plus1min_ts = 1622523347 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_anydbm.anydbmRestTest testMethod=testAuthAllowedPost> > >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 = <test.test_anydbm.anydbmRestTest testMethod=testAuthAllowedPut> > > 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 = <Permission 0x7f3918846f40 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523287 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f39186b1df0> >p = <Permission 0x7f39188466d0 'Retire','issue',None,None,None> >plus1min_ts = 1622523347 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_anydbm.anydbmRestTest testMethod=testAuthAllowedPut> > >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 = <test.test_anydbm.anydbmRestTest testMethod=testAuthDeniedPost> > > 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 = <Permission 0x7f391a86f6d0 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523288 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f39185a9e50> >p = <Permission 0x7f391864cdf0 'Retire','issue',None,None,None> >plus1min_ts = 1622523348 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_anydbm.anydbmRestTest testMethod=testAuthDeniedPost> > >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 = <test.test_anydbm.anydbmRestTest testMethod=testAuthDeniedPut> > > 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 = <Permission 0x7f39186a0490 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523288 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f3918f41220> >p = <Permission 0x7f39186a0520 'Retire','issue',None,None,None> >plus1min_ts = 1622523348 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_anydbm.anydbmRestTest testMethod=testAuthDeniedPut> > >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 = <test.test_anydbm.anydbmRestTest testMethod=testBinaryFieldStorage> > > 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 = <Permission 0x7f39189ac8e0 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523288 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f3918998100> >p = <Permission 0x7f39189ac5b0 'Retire','issue',None,None,None> >plus1min_ts = 1622523348 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_anydbm.anydbmRestTest testMethod=testBinaryFieldStorage> > >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 = <test.test_anydbm.anydbmRestTest testMethod=testDeleteAttributeUri> > > 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 = <Permission 0x7f3918668dc0 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523288 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f391865f760> >p = <Permission 0x7f3918668970 'Retire','issue',None,None,None> >plus1min_ts = 1622523348 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_anydbm.anydbmRestTest testMethod=testDeleteAttributeUri> > >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 = <test.test_anydbm.anydbmRestTest testMethod=testDispatch> > > 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 = <Permission 0x7f3917e56f10 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523289 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f3918972400> >p = <Permission 0x7f391a94c610 'Retire','issue',None,None,None> >plus1min_ts = 1622523349 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_anydbm.anydbmRestTest testMethod=testDispatch> > >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 = <test.test_anydbm.anydbmRestTest testMethod=testDispatchPost> > > 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 = <Permission 0x7f391a88d670 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523289 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f39186f1970> >p = <Permission 0x7f391a88d700 'Retire','issue',None,None,None> >plus1min_ts = 1622523349 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_anydbm.anydbmRestTest testMethod=testDispatchPost> > >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 = <test.test_anydbm.anydbmRestTest testMethod=testEtagGeneration> > > 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 = <Permission 0x7f391a974790 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523289 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f3918836bb0> >p = <Permission 0x7f391a974b80 'Retire','issue',None,None,None> >plus1min_ts = 1622523349 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_anydbm.anydbmRestTest testMethod=testEtagGeneration> > >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 = <test.test_anydbm.anydbmRestTest testMethod=testEtagProcessing> > > 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 = <Permission 0x7f3917e24eb0 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523290 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f39184e0670> >p = <Permission 0x7f3917e24130 'Retire','issue',None,None,None> >plus1min_ts = 1622523350 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_anydbm.anydbmRestTest testMethod=testEtagProcessing> > >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 = <test.test_anydbm.anydbmRestTest testMethod=testFilter> > > 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 = <Permission 0x7f391845a880 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523290 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f3918f7cf10> >p = <Permission 0x7f3918668eb0 'Retire','issue',None,None,None> >plus1min_ts = 1622523350 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_anydbm.anydbmRestTest testMethod=testFilter> > >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 = <test.test_anydbm.anydbmRestTest testMethod=testGet> > > 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 = <Permission 0x7f3918f76880 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523290 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f3917e2dbb0> >p = <Permission 0x7f3918f76160 'Retire','issue',None,None,None> >plus1min_ts = 1622523350 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_anydbm.anydbmRestTest testMethod=testGet> > >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 = <test.test_anydbm.anydbmRestTest testMethod=testGetExactMatch> > > 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 = <Permission 0x7f391803ed00 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523290 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f39185b1f70> >p = <Permission 0x7f391803e3d0 'Retire','issue',None,None,None> >plus1min_ts = 1622523350 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_anydbm.anydbmRestTest testMethod=testGetExactMatch> > >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 = <test.test_anydbm.anydbmRestTest testMethod=testGetTransitive> > > 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 = <Permission 0x7f39189d3eb0 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523290 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f39189bc520> >p = <Permission 0x7f39189d32e0 'Retire','issue',None,None,None> >plus1min_ts = 1622523350 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_anydbm.anydbmRestTest testMethod=testGetTransitive> > >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 = <test.test_anydbm.anydbmRestTest testMethod=testMethodOverride> > > 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 = <Permission 0x7f39186546d0 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523291 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f39186a1f10> >p = <Permission 0x7f3918654c40 'Retire','issue',None,None,None> >plus1min_ts = 1622523351 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_anydbm.anydbmRestTest testMethod=testMethodOverride> > >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 = <test.test_anydbm.anydbmRestTest testMethod=testOutputFormat> > > 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 = <Permission 0x7f3917f2e040 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523291 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f39186415b0> >p = <Permission 0x7f3917f2ef10 'Retire','issue',None,None,None> >plus1min_ts = 1622523351 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_anydbm.anydbmRestTest testMethod=testOutputFormat> > >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 = <test.test_anydbm.anydbmRestTest testMethod=testPagination> > > 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 = <Permission 0x7f39184a9d00 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523291 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f3917dfd880> >p = <Permission 0x7f39184a9f40 'Retire','issue',None,None,None> >plus1min_ts = 1622523351 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_anydbm.anydbmRestTest testMethod=testPagination> > >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 = <test.test_anydbm.anydbmRestTest testMethod=testPatchAction> > > 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 = <Permission 0x7f3917dcce50 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523291 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f3918b40430> >p = <Permission 0x7f3917dccd60 'Retire','issue',None,None,None> >plus1min_ts = 1622523351 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_anydbm.anydbmRestTest testMethod=testPatchAction> > >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 = <test.test_anydbm.anydbmRestTest testMethod=testPatchAdd> > > 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 = <Permission 0x7f3918833a00 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523292 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f39185b7310> >p = <Permission 0x7f39185878e0 'Retire','issue',None,None,None> >plus1min_ts = 1622523352 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_anydbm.anydbmRestTest testMethod=testPatchAdd> > >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 = <test.test_anydbm.anydbmRestTest testMethod=testPatchRemove> > > 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 = <Permission 0x7f391845afa0 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523292 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f39185eb850> >p = <Permission 0x7f391845af10 'Retire','issue',None,None,None> >plus1min_ts = 1622523352 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_anydbm.anydbmRestTest testMethod=testPatchRemove> > >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 = <test.test_anydbm.anydbmRestTest testMethod=testPatchRemoveAll> > > 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 = <Permission 0x7f39184e0820 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523292 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f3917df7bb0> >p = <Permission 0x7f39184e0190 'Retire','issue',None,None,None> >plus1min_ts = 1622523352 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_anydbm.anydbmRestTest testMethod=testPatchRemoveAll> > >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 = <test.test_anydbm.anydbmRestTest testMethod=testPatchReplace> > > 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 = <Permission 0x7f39186c77f0 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523292 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f39186306d0> >p = <Permission 0x7f39186c7160 'Retire','issue',None,None,None> >plus1min_ts = 1622523352 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_anydbm.anydbmRestTest testMethod=testPatchReplace> > >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 = <test.test_anydbm.anydbmRestTest testMethod=testPost> > > 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 = <Permission 0x7f39184b9400 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523292 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f391a879e20> >p = <Permission 0x7f39184b9e80 'Retire','issue',None,None,None> >plus1min_ts = 1622523352 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_anydbm.anydbmRestTest testMethod=testPost> > >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 = <test.test_anydbm.anydbmRestTest testMethod=testPostFile> > > 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 = <Permission 0x7f3917e9d5e0 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523293 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f39185faf70> >p = <Permission 0x7f3918630e50 'Retire','issue',None,None,None> >plus1min_ts = 1622523353 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_anydbm.anydbmRestTest testMethod=testPostFile> > >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 = <test.test_anydbm.anydbmRestTest testMethod=testPostPOE> > > 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 = <Permission 0x7f39189ac2e0 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523293 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f3918096190> >p = <Permission 0x7f39189acd30 'Retire','issue',None,None,None> >plus1min_ts = 1622523353 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_anydbm.anydbmRestTest testMethod=testPostPOE> > >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 = <test.test_anydbm.anydbmRestTest testMethod=testPutAttribute> > > 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 = <Permission 0x7f391885d1c0 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523293 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f3918f78a30> >p = <Permission 0x7f391885d5b0 'Retire','issue',None,None,None> >plus1min_ts = 1622523353 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_anydbm.anydbmRestTest testMethod=testPutAttribute> > >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 = <test.test_anydbm.anydbmRestTest testMethod=testPutElement> > > 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 = <Permission 0x7f3918f74d00 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523293 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f3917fdc310> >p = <Permission 0x7f3917dccfd0 'Retire','issue',None,None,None> >plus1min_ts = 1622523353 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_anydbm.anydbmRestTest testMethod=testPutElement> > >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 = <test.test_anydbm.anydbmRestTest testMethod=testRestRateLimit> > > 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 = <Permission 0x7f391801ea90 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523293 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f39184ec790> >p = <Permission 0x7f391801e700 'Retire','issue',None,None,None> >plus1min_ts = 1622523353 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_anydbm.anydbmRestTest testMethod=testRestRateLimit> > >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 = <test.test_anydbm.anydbmRestTest testMethod=testSorting> > > 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 = <Permission 0x7f3917edd700 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523294 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f391a994a30> >p = <Permission 0x7f3917edda30 'Retire','issue',None,None,None> >plus1min_ts = 1622523354 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_anydbm.anydbmRestTest testMethod=testSorting> > >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 = <test.test_anydbm.anydbmRestTest testMethod=testStatsGen> > > 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 = <Permission 0x7f391a974250 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523294 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f39179b9a30> >p = <Permission 0x7f391a974c70 'Retire','issue',None,None,None> >plus1min_ts = 1622523354 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_anydbm.anydbmRestTest testMethod=testStatsGen> > >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 = <test.test_anydbm.anydbmRestTest testMethod=testTransitiveField> > > 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 = <Permission 0x7f39189986a0 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523294 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f39184f98b0> >p = <Permission 0x7f3918998610 'Retire','issue',None,None,None> >plus1min_ts = 1622523354 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_anydbm.anydbmRestTest testMethod=testTransitiveField> > >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 = <test.test_anydbm.anydbmRestTest testMethod=test_bad_audience_jwt> > > 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 = <Permission 0x7f39185127c0 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523294 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f39184f94f0> >p = <Permission 0x7f391867b190 'Retire','issue',None,None,None> >plus1min_ts = 1622523354 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_anydbm.anydbmRestTest testMethod=test_bad_audience_jwt> > >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 = <test.test_anydbm.anydbmRestTest testMethod=test_bad_issue_jwt> > > 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 = <Permission 0x7f3917f677f0 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523295 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f39180465b0> >p = <Permission 0x7f3917f672b0 'Retire','issue',None,None,None> >plus1min_ts = 1622523355 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_anydbm.anydbmRestTest testMethod=test_bad_issue_jwt> > >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 = <test.test_anydbm.anydbmRestTest testMethod=test_bad_roles_jwt> > > 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 = <Permission 0x7f3918695f10 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523295 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f39186414f0> >p = <Permission 0x7f3918695460 'Retire','issue',None,None,None> >plus1min_ts = 1622523355 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_anydbm.anydbmRestTest testMethod=test_bad_roles_jwt> > >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 = <test.test_anydbm.anydbmRestTest testMethod=test_bad_subject_jwt> > > 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 = <Permission 0x7f391a879a30 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523295 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f39180961f0> >p = <Permission 0x7f391a879ac0 'Retire','issue',None,None,None> >plus1min_ts = 1622523355 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_anydbm.anydbmRestTest testMethod=test_bad_subject_jwt> > >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 = <test.test_anydbm.anydbmRestTest testMethod=test_disabled_jwt> > > 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 = <Permission 0x7f3918054880 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523295 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f3918096730> >p = <Permission 0x7f3918054b50 'Retire','issue',None,None,None> >plus1min_ts = 1622523355 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_anydbm.anydbmRestTest testMethod=test_disabled_jwt> > >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 = <test.test_anydbm.anydbmRestTest testMethod=test_expired_jwt> > > 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 = <Permission 0x7f39179b01f0 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523295 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f391864c9d0> >p = <Permission 0x7f39179b0d00 'Retire','issue',None,None,None> >plus1min_ts = 1622523355 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_anydbm.anydbmRestTest testMethod=test_expired_jwt> > >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 = <test.test_anydbm.anydbmRestTest testMethod=test_user_email_jwt> > > 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 = <Permission 0x7f3918998430 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523296 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f3918f7caf0> >p = <Permission 0x7f3918998070 'Retire','issue',None,None,None> >plus1min_ts = 1622523356 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_anydbm.anydbmRestTest testMethod=test_user_email_jwt> > >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 = <test.test_anydbm.anydbmRestTest testMethod=test_user_emailnorest_jwt> > > 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 = <Permission 0x7f391a983e50 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523296 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f3918f78fa0> >p = <Permission 0x7f391a9833a0 'Retire','issue',None,None,None> >plus1min_ts = 1622523356 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_anydbm.anydbmRestTest testMethod=test_user_emailnorest_jwt> > >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 = <test.test_anydbm.anydbmRestTest testMethod=test_user_jwt> > > 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 = <Permission 0x7f39186a1c70 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523296 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f3917fdc490> >p = <Permission 0x7f39186a1a90 'Retire','issue',None,None,None> >plus1min_ts = 1622523356 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_anydbm.anydbmRestTest testMethod=test_user_jwt> > >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 = <test.test_sqlite.sqliteRestTest testMethod=testAcceptHeaderParsing> > > 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 = <Permission 0x7f3917bf93d0 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523361 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f391787de20> >p = <Permission 0x7f391792e310 'Retire','issue',None,None,None> >plus1min_ts = 1622523421 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_sqlite.sqliteRestTest testMethod=testAcceptHeaderParsing> > >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 = <test.test_sqlite.sqliteRestTest testMethod=testAuthAllowedPost> > > 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 = <Permission 0x7f391896eb20 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523362 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f39179312e0> >p = <Permission 0x7f39186c1070 'Retire','issue',None,None,None> >plus1min_ts = 1622523422 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_sqlite.sqliteRestTest testMethod=testAuthAllowedPost> > >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 = <test.test_sqlite.sqliteRestTest testMethod=testAuthAllowedPut> > > 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 = <Permission 0x7f39186f1370 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523362 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f3917d8cd60> >p = <Permission 0x7f39186f17c0 'Retire','issue',None,None,None> >plus1min_ts = 1622523422 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_sqlite.sqliteRestTest testMethod=testAuthAllowedPut> > >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 = <test.test_sqlite.sqliteRestTest testMethod=testAuthDeniedPost> > > 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 = <Permission 0x7f3917e461f0 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523362 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f3917d96a00> >p = <Permission 0x7f3917e46cd0 'Retire','issue',None,None,None> >plus1min_ts = 1622523422 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_sqlite.sqliteRestTest testMethod=testAuthDeniedPost> > >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 = <test.test_sqlite.sqliteRestTest testMethod=testAuthDeniedPut> > > 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 = <Permission 0x7f39184a9970 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523362 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f3917f30d00> >p = <Permission 0x7f39184a9670 'Retire','issue',None,None,None> >plus1min_ts = 1622523422 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_sqlite.sqliteRestTest testMethod=testAuthDeniedPut> > >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 = <test.test_sqlite.sqliteRestTest testMethod=testBinaryFieldStorage> > > 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 = <Permission 0x7f3917d42340 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523362 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f3918096ac0> >p = <Permission 0x7f3917d42fd0 'Retire','issue',None,None,None> >plus1min_ts = 1622523422 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_sqlite.sqliteRestTest testMethod=testBinaryFieldStorage> > >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 = <test.test_sqlite.sqliteRestTest testMethod=testDeleteAttributeUri> > > 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 = <Permission 0x7f39183d25e0 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523363 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f3918416e20> >p = <Permission 0x7f39183d2b80 'Retire','issue',None,None,None> >plus1min_ts = 1622523423 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_sqlite.sqliteRestTest testMethod=testDeleteAttributeUri> > >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 = <test.test_sqlite.sqliteRestTest testMethod=testDispatch> > > 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 = <Permission 0x7f39186a0430 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523363 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f39179b0f10> >p = <Permission 0x7f39186a0f40 'Retire','issue',None,None,None> >plus1min_ts = 1622523423 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_sqlite.sqliteRestTest testMethod=testDispatch> > >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 = <test.test_sqlite.sqliteRestTest testMethod=testDispatchPost> > > 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 = <Permission 0x7f391a994a90 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523363 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f391a9949a0> >p = <Permission 0x7f391a994dc0 'Retire','issue',None,None,None> >plus1min_ts = 1622523423 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_sqlite.sqliteRestTest testMethod=testDispatchPost> > >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 = <test.test_sqlite.sqliteRestTest testMethod=testEtagGeneration> > > 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 = <Permission 0x7f3918e83cd0 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523364 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f3917dfdee0> >p = <Permission 0x7f3918025ee0 'Retire','issue',None,None,None> >plus1min_ts = 1622523424 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_sqlite.sqliteRestTest testMethod=testEtagGeneration> > >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 = <test.test_sqlite.sqliteRestTest testMethod=testEtagProcessing> > > 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 = <Permission 0x7f3917eed4c0 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523364 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f39185a9670> >p = <Permission 0x7f3917eedac0 'Retire','issue',None,None,None> >plus1min_ts = 1622523424 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_sqlite.sqliteRestTest testMethod=testEtagProcessing> > >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 = <test.test_sqlite.sqliteRestTest testMethod=testFilter> > > 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 = <Permission 0x7f39179fc9d0 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523364 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f3917e2d0a0> >p = <Permission 0x7f39179fc850 'Retire','issue',None,None,None> >plus1min_ts = 1622523424 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_sqlite.sqliteRestTest testMethod=testFilter> > >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 = <test.test_sqlite.sqliteRestTest testMethod=testGet> > > 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 = <Permission 0x7f3918376250 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523365 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f391846a6a0> >p = <Permission 0x7f39183761c0 'Retire','issue',None,None,None> >plus1min_ts = 1622523425 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_sqlite.sqliteRestTest testMethod=testGet> > >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 = <test.test_sqlite.sqliteRestTest testMethod=testGetExactMatch> > > 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 = <Permission 0x7f39184ecc40 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523365 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f39189d3a90> >p = <Permission 0x7f39184ec100 'Retire','issue',None,None,None> >plus1min_ts = 1622523425 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_sqlite.sqliteRestTest testMethod=testGetExactMatch> > >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 = <test.test_sqlite.sqliteRestTest testMethod=testGetTransitive> > > 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 = <Permission 0x7f3917926ca0 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523365 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f391847e190> >p = <Permission 0x7f391864cd30 'Retire','issue',None,None,None> >plus1min_ts = 1622523425 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_sqlite.sqliteRestTest testMethod=testGetTransitive> > >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 = <test.test_sqlite.sqliteRestTest testMethod=testMethodOverride> > > 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 = <Permission 0x7f391846a640 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523365 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f39178a10d0> >p = <Permission 0x7f391846ad00 'Retire','issue',None,None,None> >plus1min_ts = 1622523425 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_sqlite.sqliteRestTest testMethod=testMethodOverride> > >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 = <test.test_sqlite.sqliteRestTest testMethod=testOutputFormat> > > 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 = <Permission 0x7f391895f7c0 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523366 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f39178b1fa0> >p = <Permission 0x7f3917e4b730 'Retire','issue',None,None,None> >plus1min_ts = 1622523426 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_sqlite.sqliteRestTest testMethod=testOutputFormat> > >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 = <test.test_sqlite.sqliteRestTest testMethod=testPagination> > > 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 = <Permission 0x7f3917969fa0 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523366 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f39184b9700> >p = <Permission 0x7f3917969100 'Retire','issue',None,None,None> >plus1min_ts = 1622523426 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_sqlite.sqliteRestTest testMethod=testPagination> > >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 = <test.test_sqlite.sqliteRestTest testMethod=testPatchAction> > > 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 = <Permission 0x7f3917931dc0 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523366 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f3918972220> >p = <Permission 0x7f39189723a0 'Retire','issue',None,None,None> >plus1min_ts = 1622523426 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_sqlite.sqliteRestTest testMethod=testPatchAction> > >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 = <test.test_sqlite.sqliteRestTest testMethod=testPatchAdd> > > 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 = <Permission 0x7f3917e465b0 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523366 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f3917c056a0> >p = <Permission 0x7f3917e461c0 'Retire','issue',None,None,None> >plus1min_ts = 1622523426 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_sqlite.sqliteRestTest testMethod=testPatchAdd> > >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 = <test.test_sqlite.sqliteRestTest testMethod=testPatchRemove> > > 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 = <Permission 0x7f3918087100 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523366 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f39186c7fd0> >p = <Permission 0x7f39180876d0 'Retire','issue',None,None,None> >plus1min_ts = 1622523426 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_sqlite.sqliteRestTest testMethod=testPatchRemove> > >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 = <test.test_sqlite.sqliteRestTest testMethod=testPatchRemoveAll> > > 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 = <Permission 0x7f3917d7baf0 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523367 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f3917f30fd0> >p = <Permission 0x7f3917d7b820 'Retire','issue',None,None,None> >plus1min_ts = 1622523427 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_sqlite.sqliteRestTest testMethod=testPatchRemoveAll> > >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 = <test.test_sqlite.sqliteRestTest testMethod=testPatchReplace> > > 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 = <Permission 0x7f39178566a0 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523367 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f39185fab20> >p = <Permission 0x7f3917856ac0 'Retire','issue',None,None,None> >plus1min_ts = 1622523427 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_sqlite.sqliteRestTest testMethod=testPatchReplace> > >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 = <test.test_sqlite.sqliteRestTest testMethod=testPost> > > 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 = <Permission 0x7f3917e54100 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523367 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f391a94caf0> >p = <Permission 0x7f3917e54df0 'Retire','issue',None,None,None> >plus1min_ts = 1622523427 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_sqlite.sqliteRestTest testMethod=testPost> > >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 = <test.test_sqlite.sqliteRestTest testMethod=testPostFile> > > 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 = <Permission 0x7f3918f91520 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523367 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f3918bacb80> >p = <Permission 0x7f3918f91400 'Retire','issue',None,None,None> >plus1min_ts = 1622523427 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_sqlite.sqliteRestTest testMethod=testPostFile> > >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 = <test.test_sqlite.sqliteRestTest testMethod=testPostPOE> > > 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 = <Permission 0x7f3917bb1730 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523368 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f391a91af70> >p = <Permission 0x7f3917bb11c0 'Retire','issue',None,None,None> >plus1min_ts = 1622523428 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_sqlite.sqliteRestTest testMethod=testPostPOE> > >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 = <test.test_sqlite.sqliteRestTest testMethod=testPutAttribute> > > 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 = <Permission 0x7f3917ef1a00 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523368 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f3917fcafd0> >p = <Permission 0x7f3918686f10 'Retire','issue',None,None,None> >plus1min_ts = 1622523428 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_sqlite.sqliteRestTest testMethod=testPutAttribute> > >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 = <test.test_sqlite.sqliteRestTest testMethod=testPutElement> > > 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 = <Permission 0x7f39188b7eb0 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523368 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f3917ce70a0> >p = <Permission 0x7f3918630220 'Retire','issue',None,None,None> >plus1min_ts = 1622523428 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_sqlite.sqliteRestTest testMethod=testPutElement> > >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 = <test.test_sqlite.sqliteRestTest testMethod=testRestRateLimit> > > 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 = <Permission 0x7f3917b04be0 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523369 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f3918fd5f10> >p = <Permission 0x7f3917b04940 'Retire','issue',None,None,None> >plus1min_ts = 1622523429 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_sqlite.sqliteRestTest testMethod=testRestRateLimit> > >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 = <test.test_sqlite.sqliteRestTest testMethod=testSorting> > > 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 = <Permission 0x7f39185eb670 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523369 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f3918bacb80> >p = <Permission 0x7f39185ebc40 'Retire','issue',None,None,None> >plus1min_ts = 1622523429 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_sqlite.sqliteRestTest testMethod=testSorting> > >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 = <test.test_sqlite.sqliteRestTest testMethod=testStatsGen> > > 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 = <Permission 0x7f3917cb8f70 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523369 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f3918685490> >p = <Permission 0x7f3917cb8130 'Retire','issue',None,None,None> >plus1min_ts = 1622523429 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_sqlite.sqliteRestTest testMethod=testStatsGen> > >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 = <test.test_sqlite.sqliteRestTest testMethod=testTransitiveField> > > 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 = <Permission 0x7f3917cd6340 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523369 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f3917eed910> >p = <Permission 0x7f3917cd6190 'Retire','issue',None,None,None> >plus1min_ts = 1622523429 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_sqlite.sqliteRestTest testMethod=testTransitiveField> > >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 = <test.test_sqlite.sqliteRestTest testMethod=test_bad_audience_jwt> > > 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 = <Permission 0x7f39180968e0 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523370 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f39185eb490> >p = <Permission 0x7f39180965e0 'Retire','issue',None,None,None> >plus1min_ts = 1622523430 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_sqlite.sqliteRestTest testMethod=test_bad_audience_jwt> > >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 = <test.test_sqlite.sqliteRestTest testMethod=test_bad_issue_jwt> > > 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 = <Permission 0x7f39178e2580 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523370 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f3917c038b0> >p = <Permission 0x7f39178e23d0 'Retire','issue',None,None,None> >plus1min_ts = 1622523430 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_sqlite.sqliteRestTest testMethod=test_bad_issue_jwt> > >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 = <test.test_sqlite.sqliteRestTest testMethod=test_bad_roles_jwt> > > 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 = <Permission 0x7f391a994c10 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523370 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f39178b0280> >p = <Permission 0x7f391875deb0 'Retire','issue',None,None,None> >plus1min_ts = 1622523430 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_sqlite.sqliteRestTest testMethod=test_bad_roles_jwt> > >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 = <test.test_sqlite.sqliteRestTest testMethod=test_bad_subject_jwt> > > 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 = <Permission 0x7f3918376f70 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523370 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f3918e83f10> >p = <Permission 0x7f391892d640 'Retire','issue',None,None,None> >plus1min_ts = 1622523430 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_sqlite.sqliteRestTest testMethod=test_bad_subject_jwt> > >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 = <test.test_sqlite.sqliteRestTest testMethod=test_disabled_jwt> > > 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 = <Permission 0x7f39186c13a0 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523371 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f3917e2dfd0> >p = <Permission 0x7f39186c1580 'Retire','issue',None,None,None> >plus1min_ts = 1622523431 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_sqlite.sqliteRestTest testMethod=test_disabled_jwt> > >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 = <test.test_sqlite.sqliteRestTest testMethod=test_expired_jwt> > > 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 = <Permission 0x7f3917ca5820 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523371 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f39179262e0> >p = <Permission 0x7f3917ca5af0 'Retire','issue',None,None,None> >plus1min_ts = 1622523431 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_sqlite.sqliteRestTest testMethod=test_expired_jwt> > >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 = <test.test_sqlite.sqliteRestTest testMethod=test_user_email_jwt> > > 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 = <Permission 0x7f3917e5f160 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523371 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f3917cd4070> >p = <Permission 0x7f3917e5fa00 'Retire','issue',None,None,None> >plus1min_ts = 1622523431 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_sqlite.sqliteRestTest testMethod=test_user_email_jwt> > >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 = <test.test_sqlite.sqliteRestTest testMethod=test_user_emailnorest_jwt> > > 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 = <Permission 0x7f3917d1d6a0 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523371 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f3917969d00> >p = <Permission 0x7f3917d1d130 'Retire','issue',None,None,None> >plus1min_ts = 1622523431 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_sqlite.sqliteRestTest testMethod=test_user_emailnorest_jwt> > >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 = <test.test_sqlite.sqliteRestTest testMethod=test_user_jwt> > > 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 = <Permission 0x7f391788e0a0 'View','user',('id', 'realname', 'address', 'username'),None,False> >now_ts = 1622523372 >otk = <roundup.backends.sessions_dbm.OneTimeKeys object at 0x7f39183d2250> >p = <Permission 0x7f391788e460 'Retire','issue',None,None,None> >plus1min_ts = 1622523432 >secret = 'TestingTheJwtSecretTestingTheJwtSecret' >self = <test.test_sqlite.sqliteRestTest testMethod=test_user_jwt> > >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' >
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Raw
Actions:
View
Attachments on
bug 793431
:
713037
|
713040
|
713043
|
713046
|
713049
| 713052