├── tests ├── __init__.py ├── test_api │ ├── __init__.py │ └── test_api.py ├── test_cli │ ├── __init__.py │ ├── data │ │ ├── cli_plugins1 │ │ │ ├── not_plugin.omg │ │ │ ├── plugin2.py │ │ │ └── plugin1.py │ │ ├── image-build-config-empty.conf │ │ ├── cli_plugins2 │ │ │ ├── plugin2.py │ │ │ └── plugin3.py │ │ ├── image-build-indirection-config.conf │ │ └── image-build-config.conf │ ├── test_unique_path.py │ ├── test_arg_filter.py │ ├── loadcli.py │ ├── test_load_plugins.py │ ├── test_version.py │ ├── test_running_in_bg.py │ └── test_list_volumes.py ├── test_hub │ ├── __init__.py │ ├── test_db │ │ ├── __init__.py │ │ ├── test_savepoint.py │ │ ├── test_update_processor.py │ │ └── test_upsert_processor.py │ ├── test_models │ │ └── __init__.py │ ├── data │ │ ├── maven │ │ │ └── import_1 │ │ │ │ ├── build.log │ │ │ │ ├── root.log │ │ │ │ ├── state.log │ │ │ │ ├── checkout.log │ │ │ │ ├── com │ │ │ │ └── mycompany │ │ │ │ │ └── app │ │ │ │ │ └── my-app │ │ │ │ │ ├── maven-metadata.xml.md5 │ │ │ │ │ ├── maven-metadata.xml.sha1 │ │ │ │ │ ├── 1.0-SNAPSHOT │ │ │ │ │ ├── maven-metadata.xml.md5 │ │ │ │ │ ├── maven-metadata.xml.sha1 │ │ │ │ │ ├── my-app-1.0-20170927.043542-1.jar.md5 │ │ │ │ │ ├── my-app-1.0-20170927.043542-1.pom.md5 │ │ │ │ │ ├── my-app-1.0-20170927.043542-1.jar.sha1 │ │ │ │ │ ├── my-app-1.0-20170927.043542-1.pom.sha1 │ │ │ │ │ ├── my-app-1.0-20170927.043542-1.jar │ │ │ │ │ ├── my-app-1.0-20170927.043542-1.pom │ │ │ │ │ └── maven-metadata.xml │ │ │ │ │ └── maven-metadata.xml │ │ │ │ ├── my-app-1.0-SNAPSHOT-scm-sources.zip │ │ │ │ └── data.json │ │ └── rpms │ │ │ ├── header-signed.rpm │ │ │ ├── different_size_a.noarch.rpm │ │ │ ├── different_size_b.noarch.rpm │ │ │ ├── test-pkg-1.0.0-1.el7.noarch.rpm │ │ │ └── test-pkg-1.0.0-1.fc24.noarch.rpm │ ├── test_repo_delete.py │ ├── test_repo_set_state.py │ ├── test_enable_user.py │ ├── test_search.py │ ├── test_disable_user.py │ ├── test_get_win_archive.py │ ├── test_get_maven_archive.py │ ├── test_import_archive_internal.py │ ├── test_write_signed_rpm.py │ ├── test_get_verify_class.py │ ├── test_perm_operations.py │ ├── test_get_active_repos.py │ ├── test_get_last_event.py │ ├── test_get_task_children.py │ ├── test_show_opts.py │ ├── test_restart_hosts.py │ ├── test_get_build_notifications.py │ ├── test_get_build_notification_blocks.py │ ├── test_get_volume.py │ ├── test_build_image_indirection.py │ ├── test_list_cgs.py │ ├── test_new_typed_build.py │ ├── test_convert_value.py │ ├── test_list_packages_simple.py │ ├── test_create_rpm_checksum.py │ ├── test_download_task_output.py │ └── test_get_external_repo_list.py ├── test_lib │ ├── __init__.py │ ├── data │ │ ├── maven │ │ │ ├── bad_empty_config.ini │ │ │ ├── good_config.ini │ │ │ ├── bad_scmurl_config.ini │ │ │ ├── bad_type_config.ini │ │ │ ├── bad_wrapper_config.ini │ │ │ └── config.ini │ │ ├── rpms │ │ │ ├── test-src-1-1.fc24.src.rpm │ │ │ ├── test-deps-1-1.fc24.x86_64.rpm │ │ │ ├── test-files-1-1.fc27.noarch.rpm │ │ │ ├── test-nosrc-1-1.fc24.nosrc.rpm │ │ │ ├── test-nopatch-1-1.fc24.nosrc.rpm │ │ │ └── test-deps-1-1.fc24.x86_64.rpm.signed │ │ ├── mock │ │ │ ├── simple.data │ │ │ ├── internaldev.data │ │ │ ├── internaldev2.data │ │ │ ├── simple.out │ │ │ ├── internaldev.out │ │ │ └── internaldev2.out │ │ ├── cfg │ │ │ └── uni1.conf │ │ └── specs │ │ │ ├── test-src._spec │ │ │ ├── test-nosrc._spec │ │ │ ├── test-nopatch._spec │ │ │ ├── test-files._spec │ │ │ └── test-deps._spec │ ├── test_base64.py │ ├── test_gen_mock_config.py │ ├── test_check_sigmd5.py │ ├── test_context.py │ ├── test_rawheader_fields.py │ └── test_profiles.py ├── test_www │ ├── __init__.py │ ├── loadwebindex.py │ ├── test_buildinfo.py │ ├── test_freetask.py │ ├── test_resubmittask.py │ ├── test_canceltask.py │ ├── test_externalrepoinfo.py │ ├── test_userinfo.py │ ├── test_enablehost.py │ ├── test_disablehost.py │ ├── test_activesessiondelete.py │ └── test_packageinfo.py ├── test_builder │ ├── __init__.py │ ├── loadkojid.py │ └── data │ │ └── calls │ │ └── build_notif_1 │ │ ├── params.json │ │ └── message.txt ├── test_kojira │ ├── __init__.py │ └── loadkojira.py ├── test_plugins │ ├── __init__.py │ └── load_plugin.py ├── test_vm │ └── data │ │ └── koji.png └── common.py ├── cli ├── koji_cli │ ├── __init__.py │ └── Makefile ├── Makefile └── koji.conf ├── www ├── lib │ ├── kojiweb │ │ ├── __init__.py │ │ └── Makefile │ └── Makefile ├── static │ ├── images │ │ ├── 1px.gif │ │ ├── no.png │ │ ├── yes.png │ │ ├── free.png │ │ ├── init.png │ │ ├── koji.ico │ │ ├── koji.png │ │ ├── open.png │ │ ├── ready.png │ │ ├── assigned.png │ │ ├── building.png │ │ ├── canceled.png │ │ ├── closed.png │ │ ├── complete.png │ │ ├── deleted.png │ │ ├── expired.png │ │ ├── failed.png │ │ ├── unknown.png │ │ ├── waiting.png │ │ ├── initializing.png │ │ ├── bkgrnd_greydots.png │ │ ├── powered-by-koji.png │ │ ├── gray-triangle-down.gif │ │ ├── gray-triangle-up.gif │ │ ├── Makefile │ │ └── funnel.svg │ ├── debug.css │ ├── themes │ │ ├── README │ │ └── Makefile │ ├── js │ │ └── Makefile │ ├── errors │ │ ├── Makefile │ │ └── unauthorized.html │ └── Makefile ├── kojiweb │ ├── templates │ │ ├── macros.html.j2 │ │ ├── footer.html.j2 │ │ ├── Makefile │ │ ├── error.html.j2 │ │ ├── taginfo_deleted.html.j2 │ │ ├── reports.html.j2 │ │ ├── externalrepoinfo.html.j2 │ │ ├── buildtargetinfo.html.j2 │ │ └── api.html.j2 │ └── Makefile ├── Makefile └── conf │ └── Makefile ├── kojihub ├── app │ └── kojiapp.py ├── __init__.py └── Makefile ├── vm ├── kojivmd.sysconfig ├── fix_kojikamid.sh ├── kojivmd.service └── Makefile ├── .bandit.yaml ├── docs ├── README.schema └── source │ ├── CVEs │ ├── CVEs.rst │ ├── CVE-2017-1002153.rst │ ├── CVE-2024-9427.rst │ ├── CVE-2020-15856.rst │ └── CVE-2019-17109.rst │ ├── release_notes │ ├── release_notes_1.18.1.rst │ ├── release_notes_1.16.2.rst │ ├── release_notes_1.19.1.rst │ ├── release_notes_1.35.1.rst │ ├── release_notes_1.33.2.rst │ ├── release_notes_1.34.3.rst │ ├── release_notes_1.15.1.rst │ └── release_notes.rst │ ├── migrations │ ├── migrating_to_1.27.rst │ ├── migrating_to_1.15.rst │ ├── migrating_to_1.29.rst │ ├── migrating_to_1.34.rst │ ├── migrating_to_1.31.rst │ ├── migrating_to_1.32.rst │ ├── migrating_to_1.33.rst │ ├── migrating_to_1.28.rst │ ├── migrating_to_1.30.rst │ ├── migrating_to_1.20.rst │ ├── migrating_to_1.25.rst │ ├── migrating_to_1.24.rst │ ├── migrating_to_1.17.rst │ ├── migrating_to_1.26.rst │ ├── migrations.rst │ ├── migrating_to_1.21.rst │ ├── migrating_to_1.16.rst │ ├── migrating_to_1.19.rst │ ├── migrating_to_1.14.rst │ ├── migrating_to_1.23.rst │ ├── migrating_to_1.18.rst │ └── migrating_to_1.13.rst │ └── supported_platforms.rst ├── koji ├── _version.py ├── Makefile ├── server.py └── context.py ├── schemas ├── schema-upgrade-1.14-1.15.sql ├── schema-upgrade-1.28-1.29.sql ├── schema-upgrade-1.23-1.24.sql ├── schema-upgrade-1.24-1.25.sql ├── schema-update-dist-repos.sql ├── schema-upgrade-1.12-1.13.sql ├── schema-upgrade-1.11-1.12.sql ├── schema-upgrade-1.36-1.37.sql ├── schema-upgrade-1.19-1.20.sql ├── schema-upgrade-1.25-1.26.sql ├── Makefile ├── schema-upgrade-1.16-1.17.sql ├── schema-upgrade-1.13-1.14.sql ├── schema-upgrade-1.22-1.23.sql ├── schema-upgrade-1.29-1.30.sql ├── schema-upgrade-1.8-1.9.sql ├── schema-upgrade-1.20-1.21.sql ├── schema-upgrade-1.30-1.31.sql ├── schema-upgrade-1.6-1.7.sql ├── schema-upgrade-1.32-1.33.sql ├── schema-upgrade-1.31-1.32.sql ├── schema-upgrade-1.35-1.36.sql └── schema-upgrade-1.27-1.28.sql ├── plugins ├── builder │ ├── save_failed_tree.conf │ └── runroot.conf └── hub │ ├── rpm2maven.conf │ ├── save_failed_tree.conf │ ├── sidetag.conf │ ├── echo.py │ ├── protonmsg.conf │ └── dud.py ├── Authors ├── util ├── koji-shadow.conf ├── koji-sweep-db.service ├── koji-gc.service ├── koji-gc.timer ├── koji-sweep-db.timer ├── kojira.service ├── email.tpl ├── koji-gc.conf ├── Makefile └── kojira.conf ├── devtools ├── get_site_packages.py ├── fakeweb.conf.sample ├── fakehub.conf.sample ├── containers │ ├── Dockerfile.f34 │ ├── Dockerfile.f35 │ ├── Dockerfile.f36 │ ├── Dockerfile.f37 │ ├── Dockerfile.f38 │ ├── Dockerfile.f39 │ ├── Dockerfile.rawhide │ ├── Dockerfile.centos8 │ ├── Dockerfile.centos9 │ └── Dockerfile.centos7 └── README.md ├── requirements.txt ├── .coveragerc ├── .coveragerc3 ├── test-requirements.txt ├── .gitignore ├── .editorconfig ├── COPYING ├── .flake8 └── runtests /tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /cli/koji_cli/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_cli/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_hub/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_lib/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_www/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_builder/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_kojira/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_plugins/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_hub/test_db/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_hub/test_models/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_cli/data/cli_plugins1/not_plugin.omg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_lib/data/maven/bad_empty_config.ini: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_cli/data/cli_plugins1/plugin2.py: -------------------------------------------------------------------------------- 1 | sth = 123 -------------------------------------------------------------------------------- /tests/test_cli/data/image-build-config-empty.conf: -------------------------------------------------------------------------------- 1 | # empty config file -------------------------------------------------------------------------------- /tests/test_hub/data/maven/import_1/build.log: -------------------------------------------------------------------------------- 1 | This is build.log 2 | -------------------------------------------------------------------------------- /tests/test_hub/data/maven/import_1/root.log: -------------------------------------------------------------------------------- 1 | This is root.log 2 | -------------------------------------------------------------------------------- /tests/test_hub/data/maven/import_1/state.log: -------------------------------------------------------------------------------- 1 | This is state.log 2 | -------------------------------------------------------------------------------- /tests/test_hub/data/maven/import_1/checkout.log: -------------------------------------------------------------------------------- 1 | This is checkout.log 2 | -------------------------------------------------------------------------------- /www/lib/kojiweb/__init__.py: -------------------------------------------------------------------------------- 1 | # identify this directory as a python module 2 | -------------------------------------------------------------------------------- /kojihub/app/kojiapp.py: -------------------------------------------------------------------------------- 1 | from kojihub.kojixmlrpc import application # noqa: F401 2 | -------------------------------------------------------------------------------- /vm/kojivmd.sysconfig: -------------------------------------------------------------------------------- 1 | FORCE_LOCK=Y 2 | KOJIVMD_DEBUG=N 3 | KOJIVMD_VERBOSE=Y 4 | -------------------------------------------------------------------------------- /.bandit.yaml: -------------------------------------------------------------------------------- 1 | markupsafe_xss: 2 | allowed_calls: 3 | - _MarkTrustedValue 4 | -------------------------------------------------------------------------------- /docs/README.schema: -------------------------------------------------------------------------------- 1 | SQL schema and updates were moved to ../schemas (/usr/share/koji) 2 | -------------------------------------------------------------------------------- /www/static/images/1px.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koji-project/koji/HEAD/www/static/images/1px.gif -------------------------------------------------------------------------------- /www/static/images/no.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koji-project/koji/HEAD/www/static/images/no.png -------------------------------------------------------------------------------- /www/static/images/yes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koji-project/koji/HEAD/www/static/images/yes.png -------------------------------------------------------------------------------- /tests/test_vm/data/koji.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koji-project/koji/HEAD/tests/test_vm/data/koji.png -------------------------------------------------------------------------------- /www/static/images/free.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koji-project/koji/HEAD/www/static/images/free.png -------------------------------------------------------------------------------- /www/static/images/init.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koji-project/koji/HEAD/www/static/images/init.png -------------------------------------------------------------------------------- /www/static/images/koji.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koji-project/koji/HEAD/www/static/images/koji.ico -------------------------------------------------------------------------------- /www/static/images/koji.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koji-project/koji/HEAD/www/static/images/koji.png -------------------------------------------------------------------------------- /www/static/images/open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koji-project/koji/HEAD/www/static/images/open.png -------------------------------------------------------------------------------- /www/static/images/ready.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koji-project/koji/HEAD/www/static/images/ready.png -------------------------------------------------------------------------------- /koji/_version.py: -------------------------------------------------------------------------------- 1 | __version_info__ = (1, 35, 3) 2 | __version__ = '.'.join([str(x) for x in __version_info__]) 3 | -------------------------------------------------------------------------------- /www/static/images/assigned.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koji-project/koji/HEAD/www/static/images/assigned.png -------------------------------------------------------------------------------- /www/static/images/building.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koji-project/koji/HEAD/www/static/images/building.png -------------------------------------------------------------------------------- /www/static/images/canceled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koji-project/koji/HEAD/www/static/images/canceled.png -------------------------------------------------------------------------------- /www/static/images/closed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koji-project/koji/HEAD/www/static/images/closed.png -------------------------------------------------------------------------------- /www/static/images/complete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koji-project/koji/HEAD/www/static/images/complete.png -------------------------------------------------------------------------------- /www/static/images/deleted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koji-project/koji/HEAD/www/static/images/deleted.png -------------------------------------------------------------------------------- /www/static/images/expired.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koji-project/koji/HEAD/www/static/images/expired.png -------------------------------------------------------------------------------- /www/static/images/failed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koji-project/koji/HEAD/www/static/images/failed.png -------------------------------------------------------------------------------- /www/static/images/unknown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koji-project/koji/HEAD/www/static/images/unknown.png -------------------------------------------------------------------------------- /www/static/images/waiting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koji-project/koji/HEAD/www/static/images/waiting.png -------------------------------------------------------------------------------- /tests/test_hub/data/maven/import_1/com/mycompany/app/my-app/maven-metadata.xml.md5: -------------------------------------------------------------------------------- 1 | c5e046db861fb27a0a008b931b2381c4 -------------------------------------------------------------------------------- /schemas/schema-upgrade-1.14-1.15.sql: -------------------------------------------------------------------------------- 1 | -- Nothing to do 2 | -- 3 | -- There were no schema changes between 1.14 and 1.15 4 | -------------------------------------------------------------------------------- /tests/test_hub/data/maven/import_1/com/mycompany/app/my-app/maven-metadata.xml.sha1: -------------------------------------------------------------------------------- 1 | 4ed2391cce9de027b1b24ddc572feb198df7b9a3 -------------------------------------------------------------------------------- /www/static/images/initializing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koji-project/koji/HEAD/www/static/images/initializing.png -------------------------------------------------------------------------------- /tests/test_hub/data/maven/import_1/com/mycompany/app/my-app/1.0-SNAPSHOT/maven-metadata.xml.md5: -------------------------------------------------------------------------------- 1 | 3a7199598278308bc0dace024e5ad231 -------------------------------------------------------------------------------- /www/static/images/bkgrnd_greydots.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koji-project/koji/HEAD/www/static/images/bkgrnd_greydots.png -------------------------------------------------------------------------------- /www/static/images/powered-by-koji.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koji-project/koji/HEAD/www/static/images/powered-by-koji.png -------------------------------------------------------------------------------- /plugins/builder/save_failed_tree.conf: -------------------------------------------------------------------------------- 1 | [global] 2 | volume = DEFAULT 3 | 4 | [filters] 5 | paths = */tmp/krb5cc */etc/*.keytab 6 | -------------------------------------------------------------------------------- /www/static/images/gray-triangle-down.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koji-project/koji/HEAD/www/static/images/gray-triangle-down.gif -------------------------------------------------------------------------------- /www/static/images/gray-triangle-up.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koji-project/koji/HEAD/www/static/images/gray-triangle-up.gif -------------------------------------------------------------------------------- /tests/test_cli/data/cli_plugins2/plugin2.py: -------------------------------------------------------------------------------- 1 | from koji.plugin import export_cli 2 | 3 | 4 | @export_cli 5 | def foo5(): 6 | pass 7 | -------------------------------------------------------------------------------- /tests/test_hub/data/maven/import_1/com/mycompany/app/my-app/1.0-SNAPSHOT/maven-metadata.xml.sha1: -------------------------------------------------------------------------------- 1 | 5703d4c0b313fa8580000f49d3c2d54018dbabd3 -------------------------------------------------------------------------------- /tests/test_hub/data/rpms/header-signed.rpm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koji-project/koji/HEAD/tests/test_hub/data/rpms/header-signed.rpm -------------------------------------------------------------------------------- /tests/test_hub/data/maven/import_1/com/mycompany/app/my-app/1.0-SNAPSHOT/my-app-1.0-20170927.043542-1.jar.md5: -------------------------------------------------------------------------------- 1 | 1bd36fe06d1a9ee0eb1772469e922ba1 -------------------------------------------------------------------------------- /tests/test_hub/data/maven/import_1/com/mycompany/app/my-app/1.0-SNAPSHOT/my-app-1.0-20170927.043542-1.pom.md5: -------------------------------------------------------------------------------- 1 | eaa80817dcfdaab0114174a1b6a5a610 -------------------------------------------------------------------------------- /tests/test_hub/data/maven/import_1/com/mycompany/app/my-app/1.0-SNAPSHOT/my-app-1.0-20170927.043542-1.jar.sha1: -------------------------------------------------------------------------------- 1 | 66c246f8602ff2087a90196ee463ff3721305aac -------------------------------------------------------------------------------- /tests/test_hub/data/maven/import_1/com/mycompany/app/my-app/1.0-SNAPSHOT/my-app-1.0-20170927.043542-1.pom.sha1: -------------------------------------------------------------------------------- 1 | c75e355eb8a46076a680dcf050dde848e55b6d4d -------------------------------------------------------------------------------- /tests/test_lib/data/rpms/test-src-1-1.fc24.src.rpm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koji-project/koji/HEAD/tests/test_lib/data/rpms/test-src-1-1.fc24.src.rpm -------------------------------------------------------------------------------- /tests/test_hub/data/rpms/different_size_a.noarch.rpm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koji-project/koji/HEAD/tests/test_hub/data/rpms/different_size_a.noarch.rpm -------------------------------------------------------------------------------- /tests/test_hub/data/rpms/different_size_b.noarch.rpm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koji-project/koji/HEAD/tests/test_hub/data/rpms/different_size_b.noarch.rpm -------------------------------------------------------------------------------- /tests/test_lib/data/rpms/test-deps-1-1.fc24.x86_64.rpm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koji-project/koji/HEAD/tests/test_lib/data/rpms/test-deps-1-1.fc24.x86_64.rpm -------------------------------------------------------------------------------- /tests/test_lib/data/rpms/test-files-1-1.fc27.noarch.rpm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koji-project/koji/HEAD/tests/test_lib/data/rpms/test-files-1-1.fc27.noarch.rpm -------------------------------------------------------------------------------- /tests/test_lib/data/rpms/test-nosrc-1-1.fc24.nosrc.rpm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koji-project/koji/HEAD/tests/test_lib/data/rpms/test-nosrc-1-1.fc24.nosrc.rpm -------------------------------------------------------------------------------- /tests/test_hub/data/rpms/test-pkg-1.0.0-1.el7.noarch.rpm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koji-project/koji/HEAD/tests/test_hub/data/rpms/test-pkg-1.0.0-1.el7.noarch.rpm -------------------------------------------------------------------------------- /tests/test_hub/data/rpms/test-pkg-1.0.0-1.fc24.noarch.rpm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koji-project/koji/HEAD/tests/test_hub/data/rpms/test-pkg-1.0.0-1.fc24.noarch.rpm -------------------------------------------------------------------------------- /tests/test_lib/data/rpms/test-nopatch-1-1.fc24.nosrc.rpm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koji-project/koji/HEAD/tests/test_lib/data/rpms/test-nopatch-1-1.fc24.nosrc.rpm -------------------------------------------------------------------------------- /tests/test_cli/data/cli_plugins2/plugin3.py: -------------------------------------------------------------------------------- 1 | from koji.plugin import export_cli, export_as 2 | 3 | 4 | @export_as('foo6') 5 | @export_cli 6 | def foo(): 7 | pass 8 | -------------------------------------------------------------------------------- /plugins/hub/rpm2maven.conf: -------------------------------------------------------------------------------- 1 | # config file for the Koji rpm2maven plugin 2 | 3 | [patterns] 4 | rpm_names = *-repolib 5 | artifact_paths = /usr/share/java/repository/maven2/* 6 | -------------------------------------------------------------------------------- /tests/test_lib/data/mock/simple.data: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ROOTNAME", 3 | "arch": "x86_64", 4 | "managed": False, 5 | "repoid": 99, 6 | "tag_name": "TAG" 7 | } 8 | -------------------------------------------------------------------------------- /tests/test_lib/data/rpms/test-deps-1-1.fc24.x86_64.rpm.signed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koji-project/koji/HEAD/tests/test_lib/data/rpms/test-deps-1-1.fc24.x86_64.rpm.signed -------------------------------------------------------------------------------- /Authors: -------------------------------------------------------------------------------- 1 | Mike McLean 2 | Dennis Gregorovic 3 | Mike Bonnet 4 | Jesse Keating 5 | -------------------------------------------------------------------------------- /schemas/schema-upgrade-1.28-1.29.sql: -------------------------------------------------------------------------------- 1 | -- upgrade script to migrate the Koji database schema 2 | -- from version 1.28 to 1.29 3 | 4 | 5 | -- This version introduced no changes in db schema 6 | -------------------------------------------------------------------------------- /tests/test_hub/data/maven/import_1/my-app-1.0-SNAPSHOT-scm-sources.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koji-project/koji/HEAD/tests/test_hub/data/maven/import_1/my-app-1.0-SNAPSHOT-scm-sources.zip -------------------------------------------------------------------------------- /tests/test_lib/data/cfg/uni1.conf: -------------------------------------------------------------------------------- 1 | # comment ěščřčž 2 | [koji] 3 | # key_file = äöüß 4 | ; test comment with unicode 🫠 5 | # test comment with unicode ☢️ 6 | setting = hello 7 | value = world 🌍 8 | -------------------------------------------------------------------------------- /util/koji-shadow.conf: -------------------------------------------------------------------------------- 1 | # koji-shadow example config file 2 | # (still working out all the config options) 3 | 4 | [main] 5 | server=http://localhost/kojihub/ 6 | remote=https://koji.fedoraproject.org/kojihub 7 | -------------------------------------------------------------------------------- /util/koji-sweep-db.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Daily maintenance script for koji db 3 | Documentation=https://pagure.io/docs/koji/ 4 | 5 | [Service] 6 | Type=oneshot 7 | ExecStart=/usr/sbin/koji-sweep-db 8 | -------------------------------------------------------------------------------- /tests/test_lib/data/mock/internaldev.data: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ROOTNAME", 3 | "arch": "x86_64", 4 | "managed": False, 5 | "repoid": 99, 6 | "tag_name": "TAG", 7 | "internal_dev_setup": False 8 | } 9 | -------------------------------------------------------------------------------- /tests/test_lib/data/mock/internaldev2.data: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ROOTNAME", 3 | "arch": "x86_64", 4 | "managed": False, 5 | "repoid": 99, 6 | "tag_name": "TAG", 7 | "internal_dev_setup": True 8 | } 9 | -------------------------------------------------------------------------------- /www/static/debug.css: -------------------------------------------------------------------------------- 1 | /* for debugging purposes */ 2 | 3 | @import url(koji.css); 4 | 5 | * { 6 | border: 1px solid black !IMPORTANT; 7 | margin: 1px !IMPORTANT; 8 | padding: 1px !IMPORTANT; 9 | } 10 | -------------------------------------------------------------------------------- /tests/test_lib/data/specs/test-src._spec: -------------------------------------------------------------------------------- 1 | Name: test-src 2 | Version: 1 3 | Release: 1%{?dist} 4 | Summary: Testing source arch header fields 5 | 6 | License: none 7 | 8 | %description 9 | ... 10 | 11 | %files 12 | -------------------------------------------------------------------------------- /schemas/schema-upgrade-1.23-1.24.sql: -------------------------------------------------------------------------------- 1 | -- upgrade script to migrate the Koji database schema 2 | -- from version 1.23 to 1.24 3 | 4 | 5 | BEGIN; 6 | 7 | ALTER TABLE tag_external_repos ADD COLUMN arches TEXT; 8 | 9 | COMMIT; 10 | -------------------------------------------------------------------------------- /util/koji-gc.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Koji garbage collection 3 | Documentation=https://docs.pagure.org/koji/utils/ 4 | 5 | [Service] 6 | Type=oneshot 7 | ExecStart=/usr/sbin/koji-gc --lock-file=/var/lock/koji-gc.lock --exit-on-lock 8 | -------------------------------------------------------------------------------- /util/koji-gc.timer: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Daily Koji garbage collection 3 | Documentation=https://docs.pagure.org/koji/utils/ 4 | 5 | [Timer] 6 | OnCalendar=daily 7 | Persistent=true 8 | 9 | [Install] 10 | WantedBy=timers.target 11 | -------------------------------------------------------------------------------- /util/koji-sweep-db.timer: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Daily maintenance script for koji db 3 | Documentation=https://pagure.io/docs/koji/ 4 | 5 | [Timer] 6 | OnCalendar=daily 7 | Persistent=true 8 | 9 | [Install] 10 | WantedBy=timers.target 11 | -------------------------------------------------------------------------------- /schemas/schema-upgrade-1.24-1.25.sql: -------------------------------------------------------------------------------- 1 | -- upgrade script to migrate the Koji database schema 2 | -- from version 1.24 to 1.25 3 | 4 | 5 | BEGIN; 6 | 7 | ALTER TABLE repo ADD COLUMN task_id INTEGER NULL REFERENCES task(id); 8 | 9 | COMMIT; 10 | -------------------------------------------------------------------------------- /tests/test_hub/data/maven/import_1/com/mycompany/app/my-app/1.0-SNAPSHOT/my-app-1.0-20170927.043542-1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koji-project/koji/HEAD/tests/test_hub/data/maven/import_1/com/mycompany/app/my-app/1.0-SNAPSHOT/my-app-1.0-20170927.043542-1.jar -------------------------------------------------------------------------------- /docs/source/CVEs/CVEs.rst: -------------------------------------------------------------------------------- 1 | ========= 2 | Koji CVEs 3 | ========= 4 | 5 | .. toctree:: 6 | :titlesonly: 7 | 8 | CVE-2024-9427 9 | CVE-2020-15856 10 | CVE-2019-17109 11 | CVE-2018-1002161 12 | CVE-2018-1002150 13 | CVE-2017-1002153 14 | -------------------------------------------------------------------------------- /schemas/schema-update-dist-repos.sql: -------------------------------------------------------------------------------- 1 | # schema updates for dist repo feature 2 | # to be merged into schema upgrade script for next release 3 | 4 | INSERT INTO permissions (name) VALUES ('image'); 5 | 6 | ALTER TABLE repo ADD COLUMN dist BOOLEAN DEFAULT 'false'; 7 | 8 | -------------------------------------------------------------------------------- /www/static/themes/README: -------------------------------------------------------------------------------- 1 | Place static theme content under this directory 2 | 3 | Content under koji-static/theme/$NAME/ will be used instead of the normal 4 | files under koji-static/ if KojiTheme is set to $NAME. Any absent files 5 | will fall back to the normal koji-static/ path. 6 | -------------------------------------------------------------------------------- /devtools/get_site_packages.py: -------------------------------------------------------------------------------- 1 | # prints site-packages path for use in Makefiles 2 | 3 | try: 4 | from distutils.sysconfig import get_python_lib 5 | print(get_python_lib()) 6 | except ImportError: 7 | import sysconfig 8 | print(sysconfig.get_path('purelib', 'rpm_prefix')) 9 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | -e . 2 | Cheetah;python_version < '3.0' 3 | CT3;python_version >= '3.0' 4 | psycopg2-binary;python_version >= '3.0' 5 | python-multilib 6 | python-qpid-proton<0.38.0;python_version < '3.0' 7 | python-qpid-proton;python_version >= '3.0' 8 | defusedxml 9 | jinja2 10 | -------------------------------------------------------------------------------- /schemas/schema-upgrade-1.12-1.13.sql: -------------------------------------------------------------------------------- 1 | -- upgrade script to migrate the Koji database schema 2 | -- from version 1.12 to 1.13 3 | 4 | BEGIN; 5 | 6 | -- Change VARCHAR field for tag names to TEXT to allow longer tag names 7 | ALTER TABLE tag ALTER COLUMN name TYPE TEXT; 8 | 9 | COMMIT; 10 | -------------------------------------------------------------------------------- /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | data_file = .coverage2 3 | 4 | omit = 5 | setup.py 6 | /usr/* 7 | kojihub/* 8 | plugins/hub/* 9 | tests/* 10 | .tox/* 11 | 12 | [report] 13 | exclude_lines = 14 | pragma: no cover 15 | no cover: 2.x 16 | 17 | [html] 18 | directory = htmlcov/py2 19 | -------------------------------------------------------------------------------- /tests/test_lib/data/specs/test-nosrc._spec: -------------------------------------------------------------------------------- 1 | Name: test-nosrc 2 | Version: 1 3 | Release: 1%{?dist} 4 | Summary: Testing source arch header fields 5 | 6 | License: none 7 | Source0: secret.key 8 | Nosource: 0 9 | 10 | %description 11 | ... 12 | 13 | %files 14 | -------------------------------------------------------------------------------- /schemas/schema-upgrade-1.11-1.12.sql: -------------------------------------------------------------------------------- 1 | BEGIN; 2 | 3 | -- from schema-update-dist-repos.sql 4 | 5 | INSERT INTO permissions (name) VALUES ('image'); 6 | 7 | ALTER TABLE repo ADD COLUMN dist BOOLEAN; 8 | ALTER TABLE repo ALTER COLUMN dist SET DEFAULT 'false'; 9 | UPDATE repo SET dist = 'false'; 10 | 11 | COMMIT; 12 | -------------------------------------------------------------------------------- /schemas/schema-upgrade-1.36-1.37.sql: -------------------------------------------------------------------------------- 1 | -- upgrade script to migrate the Koji database schema 2 | -- from version 1.36 to 1.37 3 | 4 | BEGIN; 5 | 6 | INSERT INTO archivetypes (name, description, extensions) VALUES ('wsl', 'Compressed tarball for Windows Subsystem for Linux', 'wsl') ON CONFLICT DO NOTHING; 7 | 8 | COMMIT; 9 | -------------------------------------------------------------------------------- /tests/test_lib/data/specs/test-nopatch._spec: -------------------------------------------------------------------------------- 1 | Name: test-nopatch 2 | Version: 1 3 | Release: 1%{?dist} 4 | Summary: Testing source arch header fields 5 | 6 | License: none 7 | Patch0: secret.patch 8 | Nopatch: 0 9 | 10 | %description 11 | ... 12 | 13 | %files 14 | -------------------------------------------------------------------------------- /.coveragerc3: -------------------------------------------------------------------------------- 1 | [run] 2 | data_file = .coverage3 3 | 4 | omit = 5 | setup.py 6 | /usr/* 7 | /tmp/* 8 | tests/* 9 | devtools/* 10 | .tox/* 11 | 12 | [report] 13 | exclude_lines = 14 | pragma: no cover 15 | no cover 3.x 16 | if six.PY2 17 | 18 | [html] 19 | directory = htmlcov/py3 20 | -------------------------------------------------------------------------------- /devtools/fakeweb.conf.sample: -------------------------------------------------------------------------------- 1 | [web] 2 | SiteName = koji 3 | 4 | # Key urls 5 | KojiHubURL = http://localhost/kojihub 6 | KojiFilesURL = http://localhost/kojifiles 7 | 8 | Secret = toomanysecrets 9 | 10 | PythonDebug = on 11 | LogLevel = DEBUG 12 | 13 | # prevent web code from tweaking the path 14 | LibPath = /nosuchpath 15 | -------------------------------------------------------------------------------- /test-requirements.txt: -------------------------------------------------------------------------------- 1 | -e . 2 | -r requirements.txt 3 | mock<=2.0.0;python_version < '3.3' 4 | requests-mock;python_version >= '3.0' 5 | requests-mock<1.11.0;python_version < '3.0' 6 | coverage 7 | pytest 8 | pytest-cov;python_version >= '3.0' 9 | pytest-xdist;python_version >= '3.0' 10 | setuptools;python_version >= '3.0' 11 | -------------------------------------------------------------------------------- /schemas/schema-upgrade-1.19-1.20.sql: -------------------------------------------------------------------------------- 1 | -- upgrade script to migrate the Koji database schema 2 | -- from version 1.19 to 1.20 3 | 4 | 5 | BEGIN; 6 | 7 | -- drop potential very old constraint (https://pagure.io/koji/issue/1789) 8 | ALTER TABLE host_channels DROP CONSTRAINT IF EXISTS host_channels_host_id_key; 9 | 10 | COMMIT; 11 | -------------------------------------------------------------------------------- /plugins/hub/save_failed_tree.conf: -------------------------------------------------------------------------------- 1 | # config file for the Koji save-failed-trees plugin 2 | 3 | [permissions] 4 | # task methods for whose can be triggered buildroot export 5 | # * can be used to allow everything. In such case it must be only component 6 | # on line. Otherwise multiple values are delimited by comma. 7 | allowed_methods = buildArch 8 | -------------------------------------------------------------------------------- /www/kojiweb/templates/macros.html.j2: -------------------------------------------------------------------------------- 1 | {%- set TOGGLE = cycler('row-odd', 'row-even') %} 2 | 3 | {%- macro rowToggle(loop=None) %} 4 | {%- if loop is not none %} 5 | {{- loop.cycle('row-odd', 'row-even') }} 6 | {%- else %} 7 | {# use the global one #} 8 | {{- TOGGLE.next() }} 9 | {%- endif %} 10 | {%- endmacro %} 11 | 12 | -------------------------------------------------------------------------------- /schemas/schema-upgrade-1.25-1.26.sql: -------------------------------------------------------------------------------- 1 | -- upgrade script to migrate the Koji database schema 2 | -- from version 1.25 to 1.26 3 | 4 | 5 | BEGIN; 6 | 7 | ALTER TABLE channels ADD COLUMN description TEXT; 8 | ALTER TABLE channels ADD COLUMN enabled BOOLEAN NOT NULL DEFAULT 'true'; 9 | ALTER TABLE channels ADD COLUMN comment TEXT; 10 | 11 | COMMIT; 12 | -------------------------------------------------------------------------------- /vm/fix_kojikamid.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | awk '/^# INSERT kojikamid dup #/ {exit} {print $0}' kojikamid.py 4 | 5 | for fn in ../koji/__init__.py ../koji/daemon.py ../koji/util.py 6 | do 7 | awk '/^# END kojikamid dup #/ {p=0} p {print $0} /^# BEGIN kojikamid dup #/ {p=1}' $fn 8 | done 9 | 10 | awk 'p {print $0} /^# INSERT kojikamid dup #/ {p=1}' kojikamid.py 11 | -------------------------------------------------------------------------------- /devtools/fakehub.conf.sample: -------------------------------------------------------------------------------- 1 | [hub] 2 | 3 | # example configuration for fakehub 4 | DBName = koji 5 | DBUser = koji 6 | KojiDir = /mnt/koji 7 | DisableNotifications = True 8 | 9 | EnableMaven = True 10 | EnableWin = True 11 | 12 | CheckClientIP = False 13 | 14 | #KojiDebug = On 15 | LogLevel = DEBUG 16 | 17 | #KojiTraceback = normal 18 | KojiTraceback = extended 19 | 20 | -------------------------------------------------------------------------------- /vm/kojivmd.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Koji vm build server 3 | Documentation=https://docs.pagure.org/koji/server_howto/ 4 | 5 | After=network.target 6 | 7 | [Service] 8 | ExecStart=/usr/sbin/kojivmd \ 9 | --fg \ 10 | --force-lock \ 11 | --verbose 12 | Restart=on-failure 13 | RestartSec=60s 14 | 15 | [Install] 16 | WantedBy=multi-user.target 17 | -------------------------------------------------------------------------------- /tests/test_cli/data/cli_plugins1/plugin1.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | from koji.plugin import export_cli, export_as 3 | 4 | 5 | @export_as('foobar') 6 | @export_cli 7 | def foo(): 8 | pass 9 | 10 | 11 | @export_cli 12 | def foo2(): 13 | pass 14 | 15 | 16 | def foo3(): 17 | pass 18 | 19 | 20 | foo4 = 'foo4' 21 | 22 | 23 | class bar(): 24 | pass 25 | -------------------------------------------------------------------------------- /tests/test_lib/data/maven/good_config.ini: -------------------------------------------------------------------------------- 1 | [pkg4] 2 | scmurl=scmurl 3 | patches=patchurl 4 | specfile=specfile 5 | goals=goal1 goal2 6 | profiles=profile1 profile2 7 | packages=pkg1 pkg2 8 | jvm_options=--opt1 --opt2=val 9 | maven_options=--opt1 --opt2=val 10 | properties: p1=1 11 | p2 12 | p3=ppp3 13 | envs:e1=1 14 | e2=2 15 | buildrequires=r1 r2 16 | otheropts=others -------------------------------------------------------------------------------- /tests/test_lib/data/maven/bad_scmurl_config.ini: -------------------------------------------------------------------------------- 1 | [pkg] 2 | type=maven 3 | patches=patchurl 4 | specfile=specfile 5 | goals=goal1 goal2 6 | profiles=profile1 profile2 7 | packages=pkg1 pkg2 8 | jvm_options=--opt1 --opt2=val 9 | maven_options=--opt1 --opt2=val 10 | properties: p1=1 11 | p2 12 | p3=ppp3 13 | envs:e1=1 14 | e2=2 15 | buildrequires=r1 r2 16 | otheropts=others -------------------------------------------------------------------------------- /tests/test_hub/data/maven/import_1/com/mycompany/app/my-app/maven-metadata.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | com.mycompany.app 4 | my-app 5 | 6 | 7 | 1.0-SNAPSHOT 8 | 9 | 20170927043542 10 | 11 | 12 | -------------------------------------------------------------------------------- /tests/test_lib/data/maven/bad_type_config.ini: -------------------------------------------------------------------------------- 1 | [pkg] 2 | type=other 3 | scmurl=scmurl 4 | patches=patchurl 5 | specfile=specfile 6 | goals=goal1 goal2 7 | profiles=profile1 profile2 8 | packages=pkg1 pkg2 9 | jvm_options=--opt1 --opt2=val 10 | maven_options=--opt1 --opt2=val 11 | properties: p1=1 12 | p2 13 | p3=ppp3 14 | envs:e1=1 15 | e2=2 16 | buildrequires=r1 r2 17 | otheropts=others -------------------------------------------------------------------------------- /tests/test_lib/data/maven/bad_wrapper_config.ini: -------------------------------------------------------------------------------- 1 | [pkg] 2 | type=wrapper 3 | scmurl=scmurl 4 | patches=patchurl 5 | specfile=specfile 6 | goals=goal1 goal2 7 | profiles=profile1 profile2 8 | packages=pkg1 pkg2 9 | jvm_options=--opt1 --opt2=val 10 | maven_options=--opt1 --opt2=val 11 | properties: p1=1 12 | p2 13 | p3=ppp3 14 | envs:e1=1 15 | e2=2 16 | buildrequires=r1 r2 17 | otheropts=others -------------------------------------------------------------------------------- /www/kojiweb/templates/footer.html.j2: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | {{ localfooter }} 8 | 9 | 10 | {{ localbottom }} 11 | 12 | 13 | -------------------------------------------------------------------------------- /www/static/themes/Makefile: -------------------------------------------------------------------------------- 1 | SERVERDIR = /themes 2 | FILES = README 3 | 4 | _default: 5 | @echo "nothing to make. try make install" 6 | 7 | clean: 8 | rm -f *.o *.so *.pyc *~ 9 | 10 | install: 11 | @if [ "$(DESTDIR)" = "" ]; then \ 12 | echo " "; \ 13 | echo "ERROR: A destdir is required"; \ 14 | exit 1; \ 15 | fi 16 | 17 | mkdir -p $(DESTDIR)/$(SERVERDIR) 18 | install -p -m 644 $(FILES) $(DESTDIR)/$(SERVERDIR) 19 | -------------------------------------------------------------------------------- /docs/source/release_notes/release_notes_1.18.1.rst: -------------------------------------------------------------------------------- 1 | Koji 1.18.1 Release Notes 2 | ========================= 3 | 4 | Koji 1.18.1 is a bugfix release for Koji 1.18. 5 | The purpose of this release is address :doc:`../CVEs/CVE-2019-17109`. 6 | 7 | 8 | Issues fixed in 1.18.1 9 | ---------------------- 10 | 11 | - `Issue 1634 `_ -- 12 | possible to upload file to a path other than work directory 13 | -------------------------------------------------------------------------------- /schemas/Makefile: -------------------------------------------------------------------------------- 1 | BUILDDIR = build 2 | SQLFILES = $(wildcard *.sql) 3 | SQLDIR = /usr/share/koji 4 | 5 | clean: 6 | rm -rf $(BUILDDIR)/* 7 | 8 | install: 9 | @if [ "$(DESTDIR)" = "" ]; then \ 10 | echo " "; \ 11 | echo "ERROR: A destdir is required"; \ 12 | exit 1; \ 13 | fi 14 | 15 | mkdir -p $(DESTDIR)/$(SQLDIR) 16 | for p in $(SQLFILES) ; do \ 17 | install -p -m 644 $$p $(DESTDIR)/$(SQLDIR)/$$p; \ 18 | done 19 | -------------------------------------------------------------------------------- /www/static/js/Makefile: -------------------------------------------------------------------------------- 1 | SERVERDIR = /js 2 | FILES = $(wildcard *.js) 3 | 4 | _default: 5 | @echo "nothing to make. try make install" 6 | 7 | clean: 8 | rm -f *.o *.so *.pyc *~ 9 | 10 | install: 11 | @if [ "$(DESTDIR)" = "" ]; then \ 12 | echo " "; \ 13 | echo "ERROR: A destdir is required"; \ 14 | exit 1; \ 15 | fi 16 | 17 | mkdir -p $(DESTDIR)/$(SERVERDIR) 18 | install -p -m 644 $(FILES) $(DESTDIR)/$(SERVERDIR) 19 | -------------------------------------------------------------------------------- /devtools/containers/Dockerfile.f34: -------------------------------------------------------------------------------- 1 | FROM registry.fedoraproject.org/fedora:34 2 | RUN \ 3 | dnf -y update --nodocs --setopt=install_weak_deps=False && \ 4 | dnf install -y --nodocs --setopt=install_weak_deps=False \ 5 | gcc \ 6 | glibc-langpack-en \ 7 | krb5-devel \ 8 | python3-defusedxml \ 9 | python3-devel \ 10 | python3-librepo \ 11 | python3-setuptools \ 12 | python3-tox \ 13 | rpm-build && \ 14 | dnf clean all 15 | -------------------------------------------------------------------------------- /devtools/containers/Dockerfile.f35: -------------------------------------------------------------------------------- 1 | FROM registry.fedoraproject.org/fedora:35 2 | RUN \ 3 | dnf -y update --nodocs --setopt=install_weak_deps=False && \ 4 | dnf install -y --nodocs --setopt=install_weak_deps=False \ 5 | gcc \ 6 | glibc-langpack-en \ 7 | krb5-devel \ 8 | python3-defusedxml \ 9 | python3-devel \ 10 | python3-librepo \ 11 | python3-setuptools \ 12 | python3-tox \ 13 | rpm-build && \ 14 | dnf clean all 15 | -------------------------------------------------------------------------------- /devtools/containers/Dockerfile.f36: -------------------------------------------------------------------------------- 1 | FROM registry.fedoraproject.org/fedora:36 2 | RUN \ 3 | dnf -y update --nodocs --setopt=install_weak_deps=False && \ 4 | dnf install -y --nodocs --setopt=install_weak_deps=False \ 5 | gcc \ 6 | glibc-langpack-en \ 7 | krb5-devel \ 8 | python3-defusedxml \ 9 | python3-devel \ 10 | python3-librepo \ 11 | python3-setuptools \ 12 | python3-tox \ 13 | rpm-build && \ 14 | dnf clean all 15 | -------------------------------------------------------------------------------- /devtools/containers/Dockerfile.f37: -------------------------------------------------------------------------------- 1 | FROM registry.fedoraproject.org/fedora:37 2 | RUN \ 3 | dnf -y update --nodocs --setopt=install_weak_deps=False && \ 4 | dnf install -y --nodocs --setopt=install_weak_deps=False \ 5 | gcc \ 6 | glibc-langpack-en \ 7 | krb5-devel \ 8 | python3-defusedxml \ 9 | python3-devel \ 10 | python3-librepo \ 11 | python3-setuptools \ 12 | python3-tox \ 13 | rpm-build && \ 14 | dnf clean all 15 | -------------------------------------------------------------------------------- /devtools/containers/Dockerfile.f38: -------------------------------------------------------------------------------- 1 | FROM registry.fedoraproject.org/fedora:38 2 | RUN \ 3 | dnf -y update --nodocs --setopt=install_weak_deps=False && \ 4 | dnf install -y --nodocs --setopt=install_weak_deps=False \ 5 | gcc \ 6 | glibc-langpack-en \ 7 | krb5-devel \ 8 | python3-defusedxml \ 9 | python3-devel \ 10 | python3-librepo \ 11 | python3-setuptools \ 12 | python3-tox \ 13 | rpm-build && \ 14 | dnf clean all 15 | -------------------------------------------------------------------------------- /devtools/containers/Dockerfile.f39: -------------------------------------------------------------------------------- 1 | FROM registry.fedoraproject.org/fedora:39 2 | RUN \ 3 | dnf -y update --nodocs --setopt=install_weak_deps=False && \ 4 | dnf install -y --nodocs --setopt=install_weak_deps=False \ 5 | gcc \ 6 | glibc-langpack-en \ 7 | krb5-devel \ 8 | python3-defusedxml \ 9 | python3-devel \ 10 | python3-librepo \ 11 | python3-setuptools \ 12 | python3-tox \ 13 | rpm-build && \ 14 | dnf clean all 15 | -------------------------------------------------------------------------------- /schemas/schema-upgrade-1.16-1.17.sql: -------------------------------------------------------------------------------- 1 | -- upgrade script to migrate the Koji database schema 2 | -- from version 1.16 to 1.17 3 | 4 | 5 | BEGIN; 6 | 7 | -- Change VARCHAR field for build_target names to TEXT to allow longer names 8 | ALTER TABLE build_target ALTER COLUMN name TYPE TEXT; 9 | 10 | -- Allow different merge modes for mergerepo 11 | ALTER TABLE tag_external_repos ADD COLUMN merge_mode TEXT DEFAULT 'koji'; 12 | 13 | COMMIT; 14 | -------------------------------------------------------------------------------- /www/static/errors/Makefile: -------------------------------------------------------------------------------- 1 | SERVERDIR = /errors 2 | FILES = $(wildcard *.html) 3 | 4 | _default: 5 | @echo "nothing to make. try make install" 6 | 7 | clean: 8 | rm -f *.o *.so *.pyc *~ 9 | 10 | install: 11 | @if [ "$(DESTDIR)" = "" ]; then \ 12 | echo " "; \ 13 | echo "ERROR: A destdir is required"; \ 14 | exit 1; \ 15 | fi 16 | 17 | mkdir -p $(DESTDIR)/$(SERVERDIR) 18 | install -p -m 644 $(FILES) $(DESTDIR)/$(SERVERDIR) 19 | -------------------------------------------------------------------------------- /devtools/containers/Dockerfile.rawhide: -------------------------------------------------------------------------------- 1 | FROM registry.fedoraproject.org/fedora:rawhide 2 | RUN \ 3 | dnf -y update --nodocs --setopt=install_weak_deps=False && \ 4 | dnf install -y --nodocs --setopt=install_weak_deps=False \ 5 | gcc \ 6 | glibc-langpack-en \ 7 | krb5-devel \ 8 | python3-defusedxml \ 9 | python3-devel \ 10 | python3-librepo \ 11 | python3-setuptools \ 12 | python3-tox \ 13 | rpm-build && \ 14 | dnf clean all 15 | -------------------------------------------------------------------------------- /www/kojiweb/templates/Makefile: -------------------------------------------------------------------------------- 1 | SERVERDIR = /templates 2 | FILES = $(wildcard *.html.j2) 3 | 4 | _default: 5 | @echo "nothing to make. try make install" 6 | 7 | clean: 8 | rm -f *.o *.so *.pyc *~ 9 | 10 | install: 11 | @if [ "$(DESTDIR)" = "" ]; then \ 12 | echo " "; \ 13 | echo "ERROR: A destdir is required"; \ 14 | exit 1; \ 15 | fi 16 | 17 | mkdir -p $(DESTDIR)/$(SERVERDIR) 18 | install -p -m 644 $(FILES) $(DESTDIR)/$(SERVERDIR) 19 | -------------------------------------------------------------------------------- /util/kojira.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Koji repo administration 3 | Documentation=https://docs.pagure.org/koji/server_howto/ 4 | 5 | # If the koji-hub is on this host, httpd.service is needed first 6 | After=network.target httpd.service 7 | 8 | [Service] 9 | ExecStart=/usr/sbin/kojira \ 10 | --fg \ 11 | --force-lock \ 12 | --verbose 13 | Restart=on-failure 14 | RestartSec=60s 15 | 16 | [Install] 17 | WantedBy=multi-user.target 18 | -------------------------------------------------------------------------------- /www/static/images/Makefile: -------------------------------------------------------------------------------- 1 | SERVERDIR = /images 2 | FILES = $(wildcard *.gif *.png *.ico *.svg) 3 | 4 | _default: 5 | @echo "nothing to make. try make install" 6 | 7 | clean: 8 | rm -f *.o *.so *.pyc *~ 9 | 10 | install: 11 | @if [ "$(DESTDIR)" = "" ]; then \ 12 | echo " "; \ 13 | echo "ERROR: A destdir is required"; \ 14 | exit 1; \ 15 | fi 16 | 17 | mkdir -p $(DESTDIR)/$(SERVERDIR) 18 | install -p -m 644 $(FILES) $(DESTDIR)/$(SERVERDIR) 19 | -------------------------------------------------------------------------------- /www/kojiweb/templates/error.html.j2: -------------------------------------------------------------------------------- 1 | #include "header.html.j2" 2 | 3 |

Error

4 | 5 |
6 | {{ explanation }} 7 |
8 | 9 | #if debug_level >= 1 10 |
11 | #else 12 |
13 | #endif 14 | {{ tb_short }} 15 |
16 | 17 | #if debug_level >= 2 18 |
19 | #else 20 |
21 | #endif 22 |
23 | {{ tb_long }}
24 | 
25 |
26 | 27 | #include "footer.html.j2" 28 | -------------------------------------------------------------------------------- /docs/source/migrations/migrating_to_1.27.rst: -------------------------------------------------------------------------------- 1 | Migrating to Koji 1.27 2 | ====================== 3 | 4 | You should consider the following changes when migrating to 1.27: 5 | 6 | DB Updates 7 | ---------- 8 | 9 | This release doesn't include any schema change. 10 | 11 | Other changes 12 | ------------- 13 | 14 | There are numerous other changes in 1.27 that should not have a direct impact on migration. For 15 | details see: :doc:`../release_notes/release_notes_1.27` 16 | -------------------------------------------------------------------------------- /tests/test_cli/data/image-build-indirection-config.conf: -------------------------------------------------------------------------------- 1 | [image-build-indirection] 2 | name = fedora-server-docker 3 | version = 26 4 | release = 1 5 | target = f26-candidate 6 | arch = x86_64 7 | 8 | base_image_build = fedora-base 9 | utility_image_build = fedora-utility 10 | indirection_template = fedora-indirection.tdl 11 | indirection_template_url = git://git.fedorahosted.org/git/indirection-templates.git?fedora26#68c40eb7 12 | results_loc = fedora-indirection.tar 13 | -------------------------------------------------------------------------------- /docs/source/release_notes/release_notes_1.16.2.rst: -------------------------------------------------------------------------------- 1 | Koji 1.16.2 Release Notes 2 | ========================= 3 | 4 | Koji 1.16.2 is a bugfix release for Koji 1.16. 5 | The purpose of this release is address :doc:`../CVEs/CVE-2018-1002161`. 6 | 7 | See also: 8 | 9 | - :doc:`release_notes_1.16.1` 10 | 11 | - :doc:`release_notes_1.16` 12 | 13 | 14 | Issues fixed in 1.16.2 15 | ---------------------- 16 | 17 | - `Issue 1183 `_ -- 18 | CVE-2018-1002161 19 | -------------------------------------------------------------------------------- /tests/test_lib/data/specs/test-files._spec: -------------------------------------------------------------------------------- 1 | Name: test-files 2 | Version: 1 3 | Release: 1%{?dist} 4 | Summary: Testing files header fields 5 | 6 | License: none 7 | 8 | %description 9 | Testing files header fields 10 | 11 | %install 12 | rm -rf $RPM_BUILD_ROOT 13 | install -d $RPM_BUILD_ROOT/foo/bar 14 | install -pm 0755 fileA $RPM_BUILD_ROOT 15 | install -pm 0600 foo/bar/fileB $RPM_BUILD_ROOT/foo/bar 16 | 17 | %files 18 | %defattr(-,root,root) 19 | /fileA 20 | /foo/bar/fileB 21 | -------------------------------------------------------------------------------- /www/kojiweb/templates/taginfo_deleted.html.j2: -------------------------------------------------------------------------------- 1 | #include "header.html.j2" 2 | 3 |

Information for deleted tag {{ tag.name }}

4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
Name{{ tag.name }}
ID{{ tag.id }}
Deleted{{ util.formatTimeLong(delete_ts) }}
16 | 17 | #include "footer.html.j2" 18 | -------------------------------------------------------------------------------- /tests/test_hub/test_repo_delete.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | import koji 4 | import kojihub 5 | 6 | 7 | class TestRepoDelete(unittest.TestCase): 8 | 9 | def test_repo_delete_wrong_type_typeID(self): 10 | repo_id = 'test-repo-id' 11 | with self.assertRaises(koji.ParameterError) as cm: 12 | kojihub.repo_delete(repo_id) 13 | self.assertEqual(f"Invalid type for value '{repo_id}': {type(repo_id)}, " 14 | f"expected type ", str(cm.exception)) 15 | -------------------------------------------------------------------------------- /devtools/containers/Dockerfile.centos8: -------------------------------------------------------------------------------- 1 | FROM quay.io/centos/centos:stream8 2 | 3 | RUN \ 4 | dnf -y update --nodocs --setopt=install_weak_deps=False && \ 5 | dnf install -y --nodocs --setopt=install_weak_deps=False epel-release && \ 6 | dnf install -y --nodocs --setopt=install_weak_deps=False \ 7 | gcc \ 8 | glibc-langpack-en \ 9 | krb5-devel \ 10 | python3-defusedxml \ 11 | python3-devel \ 12 | python3-librepo \ 13 | python3-setuptools \ 14 | python3-tox \ 15 | rpm-build && \ 16 | dnf clean all 17 | -------------------------------------------------------------------------------- /www/Makefile: -------------------------------------------------------------------------------- 1 | SUBDIRS = kojiweb conf lib static 2 | 3 | _default: 4 | @echo "nothing to make. try make install" 5 | 6 | clean: 7 | rm -f *.o *.so *.pyc *~ 8 | for d in $(SUBDIRS); do make -s -C $$d clean; done 9 | 10 | install: 11 | @if [ "$(DESTDIR)" = "" ]; then \ 12 | echo " "; \ 13 | echo "ERROR: A destdir is required"; \ 14 | exit 1; \ 15 | fi 16 | 17 | mkdir -p $(DESTDIR)/usr/share/koji-web 18 | 19 | for d in $(SUBDIRS); do make DESTDIR=$(DESTDIR) \ 20 | -C $$d install; [ $$? = 0 ] || exit 1; done 21 | -------------------------------------------------------------------------------- /www/lib/Makefile: -------------------------------------------------------------------------------- 1 | SUBDIRS = kojiweb 2 | 3 | SERVERDIR = /usr/share/koji-web/lib 4 | 5 | _default: 6 | @echo "nothing to make. try make install" 7 | 8 | clean: 9 | rm -f *.o *.so *.pyc *~ 10 | for d in $(SUBDIRS); do make -s -C $$d clean; done 11 | 12 | install: 13 | @if [ "$(DESTDIR)" = "" ]; then \ 14 | echo " "; \ 15 | echo "ERROR: A destdir is required"; \ 16 | exit 1; \ 17 | fi 18 | 19 | for d in $(SUBDIRS); do make DESTDIR=$(DESTDIR)/$(SERVERDIR) \ 20 | -C $$d install; [ $$? = 0 ] || exit 1; done 21 | -------------------------------------------------------------------------------- /docs/source/migrations/migrating_to_1.15.rst: -------------------------------------------------------------------------------- 1 | Migrating to Koji 1.15 2 | ====================== 3 | 4 | .. 5 | reStructured Text formatted 6 | 7 | The update from to 1.15 is comparatively light from a migration perspective. 8 | 9 | DB Updates 10 | ---------- 11 | 12 | There are no schema updates in 1.15. 13 | 14 | 15 | Other changes 16 | ------------- 17 | 18 | There are numerous other changes in 1.15 that should not have a direct impact 19 | on migration. For details see: 20 | :doc:`../release_notes/release_notes_1.15` 21 | -------------------------------------------------------------------------------- /tests/test_hub/test_repo_set_state.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | import koji 4 | import kojihub 5 | 6 | 7 | class TestRepoSetState(unittest.TestCase): 8 | 9 | def test_set_state_wrong_type_typeID(self): 10 | repo_id = 'test-repo-id' 11 | with self.assertRaises(koji.ParameterError) as cm: 12 | kojihub.repo_set_state(repo_id, 'failed') 13 | self.assertEqual(f"Invalid type for value '{repo_id}': {type(repo_id)}, " 14 | f"expected type ", str(cm.exception)) 15 | -------------------------------------------------------------------------------- /devtools/containers/Dockerfile.centos9: -------------------------------------------------------------------------------- 1 | FROM quay.io/centos/centos:stream9 2 | 3 | RUN \ 4 | dnf -y update --nodocs --setopt=install_weak_deps=False && \ 5 | dnf install -y --nodocs --setopt=install_weak_deps=False epel-release && \ 6 | dnf install -y --nodocs --setopt=install_weak_deps=False --enablerepo crb \ 7 | gcc \ 8 | glibc-langpack-en \ 9 | krb5-devel \ 10 | python3-defusedxml \ 11 | python3-devel \ 12 | python3-librepo \ 13 | python3-setuptools \ 14 | python3-tox \ 15 | rpm-build && \ 16 | dnf clean all 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # 2 | # python compilation artifacts 3 | *.pyc 4 | *.pyo 5 | cli/kojic 6 | builder/kojidc 7 | util/kojirac 8 | util/koji-gcc 9 | util/koji-shadowc 10 | # 11 | # coverage artifacts 12 | .coverage 13 | .coverage[23] 14 | htmlcov/ 15 | *,cover 16 | # 17 | # build artifacts 18 | koji-[0-9]*.tar.bz2 19 | koji-[0-9]*.src.rpm 20 | koji-[0-9]*[0-9]/ 21 | koji.egg-info/ 22 | noarch/ 23 | docs/build/ 24 | build/ 25 | dist/ 26 | # 27 | # misc 28 | *.swp 29 | .idea 30 | .vscode 31 | .DS_STORE 32 | .vagrant 33 | .gitreview 34 | devtools/*.conf 35 | .tox 36 | -------------------------------------------------------------------------------- /docs/source/migrations/migrating_to_1.29.rst: -------------------------------------------------------------------------------- 1 | Migrating to Koji 1.29 2 | ====================== 3 | 4 | You should consider the following changes when migrating to 1.29: 5 | 6 | DB Updates 7 | ---------- 8 | 9 | There is no schema change this time. 10 | 11 | As in previous releases, we provide a migration script which is no-op in this case. 12 | 13 | Other changes 14 | ------------- 15 | 16 | There are numerous other changes in 1.29 that should not have a direct impact on migration. For 17 | details see: :doc:`../release_notes/release_notes_1.29` 18 | -------------------------------------------------------------------------------- /devtools/containers/Dockerfile.centos7: -------------------------------------------------------------------------------- 1 | FROM quay.io/centos/centos:7 2 | RUN \ 3 | yum -y update && \ 4 | yum install -y \ 5 | dnf \ 6 | dnf-plugins-core \ 7 | gcc \ 8 | krb5-devel \ 9 | libffi-devel \ 10 | python-devel \ 11 | python-librepo \ 12 | python-requests \ 13 | rpm-build && \ 14 | yum install -y epel-release && \ 15 | yum install -y \ 16 | python-defusedxml \ 17 | python-pip \ 18 | python-psycopg2 && \ 19 | yum clean all && \ 20 | pip install -U 'pip==9.0.1' && \ 21 | pip install -U tox 22 | -------------------------------------------------------------------------------- /schemas/schema-upgrade-1.13-1.14.sql: -------------------------------------------------------------------------------- 1 | -- upgrade script to migrate the Koji database schema 2 | -- from version 1.13 to 1.14 3 | 4 | BEGIN; 5 | 6 | -- drop unused log_messages table 7 | DROP TABLE log_messages; 8 | 9 | -- add yaml and xjb file type in archivetypes 10 | insert into archivetypes (name, description, extensions) values ('yaml', 'YAML Ain''t Markup Language', 'yaml yml'); 11 | insert into archivetypes (name, description, extensions) values ('xjb', 'JAXB(Java Architecture for XML Binding) Binding Customization File', 'xjb'); 12 | 13 | COMMIT; 14 | -------------------------------------------------------------------------------- /util/email.tpl: -------------------------------------------------------------------------------- 1 | The following build(s) are unreferenced and have been marked for 2 | deletion. They will be held in the trashcan tag for a grace period. 3 | At the end of that period they will be deleted permanently. This 4 | garbage collection is a normal part of build system operation. 5 | Please see the following url for more information: 6 | 7 | https://fedoraproject.org/wiki/Koji/GarbageCollection 8 | 9 | ${builds} 10 | 11 | If you would like to protect any of these builds from deletion, please 12 | refer to the document linked above for instructions. 13 | -------------------------------------------------------------------------------- /www/conf/Makefile: -------------------------------------------------------------------------------- 1 | _default: 2 | @echo "nothing to make. try make install" 3 | 4 | clean: 5 | rm -f *.o *.so *.pyc *~ 6 | for d in $(SUBDIRS); do make -s -C $$d clean; done 7 | 8 | install: 9 | @if [ "$(DESTDIR)" = "" ]; then \ 10 | echo " "; \ 11 | echo "ERROR: A destdir is required"; \ 12 | exit 1; \ 13 | fi 14 | 15 | mkdir -p $(DESTDIR)/etc/httpd/conf.d 16 | install -p -m 644 kojiweb.conf $(DESTDIR)/etc/httpd/conf.d/kojiweb.conf 17 | 18 | mkdir -p $(DESTDIR)/etc/kojiweb 19 | install -p -m 644 web.conf $(DESTDIR)/etc/kojiweb/web.conf 20 | mkdir -p $(DESTDIR)/etc/kojiweb/web.conf.d 21 | -------------------------------------------------------------------------------- /plugins/hub/sidetag.conf: -------------------------------------------------------------------------------- 1 | [sidetag] 2 | # automatically remove sidetag on untagging last package 3 | remove_empty = off 4 | 5 | # potential suffixes for sidetag names 6 | # allowed_suffixes = 7 | 8 | # template for sidetag names. It must contain basetag and tag_id parts 9 | # if allowed_suffixes is not empty and suffix was requested, it will be added 10 | # as {name_template}-{suffix}. (percent-signs need to be escaped) 11 | # name_template = {basetag}-side-{tag_id} 12 | 13 | # Automaticaly trigger newRepo task for every new sidetag. Otherwise let kojira 14 | # prioritize these. 15 | # trigger_new_repo = False 16 | -------------------------------------------------------------------------------- /tests/test_www/loadwebindex.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | try: 5 | import importlib 6 | import importlib.machinery 7 | except ImportError: 8 | import imp 9 | importlib = None 10 | 11 | INDEX_MOD = "index_fake" 12 | INDEX_FILENAME = os.path.dirname(__file__) + "/../../www/kojiweb/index.py" 13 | 14 | if importlib: 15 | spec = importlib.util.spec_from_file_location(INDEX_MOD, INDEX_FILENAME) 16 | webidx = importlib.util.module_from_spec(spec) 17 | sys.modules[INDEX_MOD] = webidx 18 | spec.loader.exec_module(webidx) 19 | else: 20 | webidx = imp.load_source(INDEX_MOD, INDEX_FILENAME) 21 | -------------------------------------------------------------------------------- /plugins/hub/echo.py: -------------------------------------------------------------------------------- 1 | # Example Koji callback 2 | # Copyright (c) 2009-2014 Red Hat, Inc. 3 | # This callback simply logs all of its args using the logging module 4 | # 5 | # Authors: 6 | # Mike Bonnet 7 | 8 | import logging 9 | 10 | from koji.plugin import callback, callbacks, ignore_error 11 | from koji.util import to_list 12 | 13 | 14 | @callback(*to_list(callbacks.keys())) 15 | @ignore_error 16 | def echo(cbtype, *args, **kws): 17 | logging.getLogger('koji.plugin.echo').info('Called the %s callback, args: %s; kws: %s', 18 | cbtype, str(args), str(kws)) 19 | -------------------------------------------------------------------------------- /schemas/schema-upgrade-1.22-1.23.sql: -------------------------------------------------------------------------------- 1 | -- upgrade script to migrate the Koji database schema 2 | -- from version 1.22 to 1.23 3 | 4 | 5 | BEGIN; 6 | 7 | CREATE INDEX task_by_no_parent_state_method ON task(parent, state, method) WHERE parent IS NULL; 8 | 9 | ALTER TABLE tag_extra ALTER COLUMN value DROP NOT NULL; 10 | 11 | -- Message queue for the protonmsg plugin 12 | CREATE TABLE proton_queue ( 13 | id SERIAL PRIMARY KEY, 14 | created_ts TIMESTAMPTZ DEFAULT NOW(), 15 | address TEXT NOT NULL, 16 | props JSON NOT NULL, 17 | body JSON NOT NULL 18 | ) WITHOUT OIDS; 19 | 20 | 21 | COMMIT; 22 | -------------------------------------------------------------------------------- /docs/source/migrations/migrating_to_1.34.rst: -------------------------------------------------------------------------------- 1 | Migrating to Koji 1.34 2 | ====================== 3 | 4 | You should consider the following changes when migrating to 1.34: 5 | 6 | DB Updates 7 | ---------- 8 | 9 | There is a simple schema change adding new index. 10 | 11 | As in previous releases, we provide a migration script that updates the database. 12 | 13 | :: 14 | 15 | # psql koji koji < /usr/share/koji/schema-upgrade-1.33-1.34.sql 16 | 17 | 18 | Other changes 19 | ------------- 20 | 21 | There are numerous other changes in 1.34 that should not have a direct impact on migration. For 22 | details see: :doc:`../release_notes/release_notes_1.34` 23 | -------------------------------------------------------------------------------- /docs/source/migrations/migrating_to_1.31.rst: -------------------------------------------------------------------------------- 1 | Migrating to Koji 1.31 2 | ====================== 3 | 4 | You should consider the following changes when migrating to 1.31: 5 | 6 | DB Updates 7 | ---------- 8 | 9 | There is a simple schema change adding new index. 10 | 11 | As in previous releases, we provide a migration script that updates the database. 12 | 13 | :: 14 | 15 | # psql koji koji < /usr/share/doc/koji/docs/schema-upgrade-1.30-1.31.sql 16 | 17 | 18 | Other changes 19 | ------------- 20 | 21 | There are numerous other changes in 1.31 that should not have a direct impact on migration. For 22 | details see: :doc:`../release_notes/release_notes_1.31` 23 | -------------------------------------------------------------------------------- /docs/source/migrations/migrating_to_1.32.rst: -------------------------------------------------------------------------------- 1 | Migrating to Koji 1.32 2 | ====================== 3 | 4 | You should consider the following changes when migrating to 1.32: 5 | 6 | DB Updates 7 | ---------- 8 | 9 | There is a simple schema change adding new index. 10 | 11 | As in previous releases, we provide a migration script that updates the database. 12 | 13 | :: 14 | 15 | # psql koji koji < /usr/share/doc/koji/docs/schema-upgrade-1.31-1.32.sql 16 | 17 | 18 | Other changes 19 | ------------- 20 | 21 | There are numerous other changes in 1.32 that should not have a direct impact on migration. For 22 | details see: :doc:`../release_notes/release_notes_1.32` 23 | -------------------------------------------------------------------------------- /docs/source/migrations/migrating_to_1.33.rst: -------------------------------------------------------------------------------- 1 | Migrating to Koji 1.33 2 | ====================== 3 | 4 | You should consider the following changes when migrating to 1.33: 5 | 6 | DB Updates 7 | ---------- 8 | 9 | There is a simple schema change adding new index. 10 | 11 | As in previous releases, we provide a migration script that updates the database. 12 | 13 | :: 14 | 15 | # psql koji koji < /usr/share/doc/koji/docs/schema-upgrade-1.32-1.33.sql 16 | 17 | 18 | Other changes 19 | ------------- 20 | 21 | There are numerous other changes in 1.33 that should not have a direct impact on migration. For 22 | details see: :doc:`../release_notes/release_notes_1.33` 23 | -------------------------------------------------------------------------------- /docs/source/CVEs/CVE-2017-1002153.rst: -------------------------------------------------------------------------------- 1 | ================ 2 | CVE-2017-1002153 3 | ================ 4 | 5 | Koji 1.13.0 does not properly validate SCM paths. 6 | 7 | 8 | Summary 9 | ------- 10 | 11 | Koji 1.13.0 does not properly validate SCM paths, allowing an attacker to work around blacklisted paths for build submission. 12 | 13 | 14 | Bug fix 15 | ------- 16 | 17 | Koji versions 1.14.0 and forward contain the fix. 18 | 19 | This bug was tracked as `issue#563 `_ 20 | 21 | Links 22 | ----- 23 | 24 | Fixed versions can be found at our releases page: 25 | 26 | `https://pagure.io/koji/releases `_ 27 | -------------------------------------------------------------------------------- /www/static/Makefile: -------------------------------------------------------------------------------- 1 | SUBDIRS = images errors js themes 2 | 3 | SERVERDIR = /usr/share/koji-web/static 4 | FILES = $(wildcard *.css) 5 | 6 | _default: 7 | @echo "nothing to make. try make install" 8 | 9 | clean: 10 | rm -f *.o *.so *.pyc *~ 11 | for d in $(SUBDIRS); do make -s -C $$d clean; done 12 | 13 | install: 14 | @if [ "$(DESTDIR)" = "" ]; then \ 15 | echo " "; \ 16 | echo "ERROR: A destdir is required"; \ 17 | exit 1; \ 18 | fi 19 | 20 | mkdir -p $(DESTDIR)/$(SERVERDIR) 21 | install -p -m 644 $(FILES) $(DESTDIR)/$(SERVERDIR) 22 | 23 | for d in $(SUBDIRS); do make DESTDIR=$(DESTDIR)/$(SERVERDIR) \ 24 | -C $$d install; [ $$? = 0 ] || exit 1; done 25 | -------------------------------------------------------------------------------- /schemas/schema-upgrade-1.29-1.30.sql: -------------------------------------------------------------------------------- 1 | -- upgrade script to migrate the Koji database schema 2 | -- from version 1.29 to 1.30 3 | 4 | 5 | BEGIN; 6 | 7 | ALTER TABLE archivetypes ADD COLUMN compression_type TEXT; 8 | 9 | UPDATE archivetypes set compression_type='zip' WHERE name = 'jar'; 10 | UPDATE archivetypes set compression_type='zip' WHERE name = 'zip'; 11 | UPDATE archivetypes set compression_type='tar' WHERE name = 'tar'; 12 | 13 | -- clean some unused old indices if they still exist 14 | -- https://pagure.io/koji/issue/3160 15 | DROP INDEX IF EXISTS image_listing_archives; 16 | DROP INDEX IF EXISTS image_listing_rpms; 17 | DROP INDEX IF EXISTS imageinfo_listing_rpms; 18 | 19 | COMMIT; 20 | -------------------------------------------------------------------------------- /www/kojiweb/Makefile: -------------------------------------------------------------------------------- 1 | SUBDIRS = templates 2 | 3 | SERVERDIR = /usr/share/koji-web/scripts 4 | FILES = $(wildcard *.py *.html.j2) 5 | 6 | _default: 7 | @echo "nothing to make. try make install" 8 | 9 | clean: 10 | rm -f *.o *.so *.pyc *~ 11 | rm -rf __pycache__ 12 | for d in $(SUBDIRS); do make -s -C $$d clean; done 13 | 14 | install: 15 | @if [ "$(DESTDIR)" = "" ]; then \ 16 | echo " "; \ 17 | echo "ERROR: A destdir is required"; \ 18 | exit 1; \ 19 | fi 20 | 21 | mkdir -p $(DESTDIR)/$(SERVERDIR) 22 | install -p -m 644 $(FILES) $(DESTDIR)/$(SERVERDIR) 23 | 24 | for d in $(SUBDIRS); do make DESTDIR=$(DESTDIR)/$(SERVERDIR) \ 25 | -C $$d install; [ $$? = 0 ] || exit 1; done 26 | -------------------------------------------------------------------------------- /docs/source/migrations/migrating_to_1.28.rst: -------------------------------------------------------------------------------- 1 | Migrating to Koji 1.28 2 | ====================== 3 | 4 | You should consider the following changes when migrating to 1.28: 5 | 6 | DB Updates 7 | ---------- 8 | 9 | There is a simple schema change adding descriptions to individual permissions. 10 | 11 | As in previous releases, we provide a migration script that updates the database. 12 | 13 | :: 14 | 15 | # psql koji koji < /usr/share/doc/koji/docs/schema-upgrade-1.27-1.28.sql 16 | 17 | 18 | Other changes 19 | ------------- 20 | 21 | There are numerous other changes in 1.28 that should not have a direct impact on migration. For 22 | details see: :doc:`../release_notes/release_notes_1.28` 23 | -------------------------------------------------------------------------------- /docs/source/migrations/migrating_to_1.30.rst: -------------------------------------------------------------------------------- 1 | Migrating to Koji 1.30 2 | ====================== 3 | 4 | You should consider the following changes when migrating to 1.30: 5 | 6 | DB Updates 7 | ---------- 8 | 9 | There is a simple schema change adding descriptions to individual permissions. 10 | 11 | As in previous releases, we provide a migration script that updates the database. 12 | 13 | :: 14 | 15 | # psql koji koji < /usr/share/doc/koji/docs/schema-upgrade-1.29-1.30.sql 16 | 17 | 18 | Other changes 19 | ------------- 20 | 21 | There are numerous other changes in 1.30 that should not have a direct impact on migration. For 22 | details see: :doc:`../release_notes/release_notes_1.30` 23 | -------------------------------------------------------------------------------- /cli/koji_cli/Makefile: -------------------------------------------------------------------------------- 1 | PYFILES = $(wildcard *.py) 2 | 3 | PACKAGE = $(shell basename `pwd`) 4 | PKGDIR = $(shell $(PYTHON) ../../devtools/get_site_packages.py )/$(PACKAGE) 5 | 6 | _default: 7 | @echo "nothing to make. try make install" 8 | 9 | clean: 10 | rm -f *.o *.so *.pyc *~ 11 | rm -rf __pycache__ 12 | 13 | install: 14 | @if [ "$(DESTDIR)" = "" ]; then \ 15 | echo " "; \ 16 | echo "ERROR: A destdir is required"; \ 17 | exit 1; \ 18 | fi 19 | 20 | mkdir -p $(DESTDIR)/$(PKGDIR) 21 | for p in $(PYFILES) ; do \ 22 | install -p -m 644 $$p $(DESTDIR)/$(PKGDIR)/$$p; \ 23 | done 24 | 25 | $(PYTHON) -c "import compileall; compileall.compile_dir('$(DESTDIR)/$(PKGDIR)', 1, '$(PYDIR)', 1)" 26 | -------------------------------------------------------------------------------- /schemas/schema-upgrade-1.8-1.9.sql: -------------------------------------------------------------------------------- 1 | 2 | BEGIN; 3 | 4 | -- new archive types 5 | insert into archivetypes (name, description, extensions) values ('vmdk', 'vSphere image', 'vmdk'); 6 | insert into archivetypes (name, description, extensions) values ('ova', 'OVA image', 'ova'); 7 | insert into archivetypes (name, description, extensions) values ('ks', 'Kickstart', 'ks'); 8 | insert into archivetypes (name, description, extensions) values ('cfg', 'Configuration file', 'cfg'); 9 | 10 | COMMIT; 11 | 12 | BEGIN; 13 | -- it's harmless if this part fails. 14 | -- there shouldn't be any references to this, but keep it in a separate transaction just in case 15 | delete from archivetypes where name = 'vmx'; 16 | COMMIT; 17 | -------------------------------------------------------------------------------- /tests/test_builder/loadkojid.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | import os 3 | import sys 4 | 5 | # http://stackoverflow.com/questions/67631/how-to-import-a-module-given-the-full-path 6 | KOJID_FILENAME = os.path.dirname(__file__) + "/../../builder/kojid" 7 | if sys.version_info[0] >= 3: 8 | import importlib.util 9 | import importlib.machinery 10 | loader = importlib.machinery.SourceFileLoader('koji_kojid', KOJID_FILENAME) 11 | spec = importlib.util.spec_from_file_location("koji_kojid", loader=loader) 12 | kojid = importlib.util.module_from_spec(spec) 13 | spec.loader.exec_module(kojid) 14 | else: 15 | import imp 16 | kojid = imp.load_source('koji_kojid', KOJID_FILENAME) 17 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Topmost editorconfig for project, don't ascend 2 | # to parent directories when scanning configs 3 | root = true 4 | 5 | # All files: UTF-8 with Unix-style newlines, 6 | # no trailing whitespace, and a final newline 7 | [*] 8 | charset = utf-8 9 | end_of_line = lf 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | 13 | # JavaScript, CSS, Python, shell: 4-space indents 14 | [{**/*.{js,css,py,sh},runtests,cli/koji,vm/kojivmd}] 15 | indent_style = space 16 | indent_size = 4 17 | 18 | # Makefile: tab indents 19 | [**/Makefile] 20 | indent_style = tab 21 | tab_width = 8 22 | 23 | # jinja html: 2-space indents 24 | [**/*.html.j2] 25 | indent_style = space 26 | indent_size = 2 27 | 28 | -------------------------------------------------------------------------------- /schemas/schema-upgrade-1.20-1.21.sql: -------------------------------------------------------------------------------- 1 | -- upgrade script to migrate the Koji database schema 2 | -- from version 1.20 to 1.21 3 | 4 | 5 | BEGIN; 6 | 7 | -- make better events 8 | ALTER TABLE events ALTER COLUMN time SET NOT NULL; 9 | ALTER TABLE events ALTER COLUMN time SET DEFAULT clock_timestamp(); 10 | 11 | CREATE OR REPLACE FUNCTION get_event() RETURNS INTEGER AS ' 12 | INSERT INTO events (time) VALUES (clock_timestamp()); 13 | SELECT currval(''events_id_seq'')::INTEGER; 14 | ' LANGUAGE SQL; 15 | 16 | -- merge_mode can not be null 17 | UPDATE tag_external_repos SET merge_mode = 'koji' WHERE merge_mode is NULL; 18 | ALTER TABLE tag_external_repos ALTER COLUMN merge_mode SET NOT NULL; 19 | 20 | COMMIT; 21 | -------------------------------------------------------------------------------- /tests/test_hub/test_enable_user.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from unittest import mock 4 | 5 | import koji 6 | import kojihub 7 | 8 | 9 | class TestEnableUser(unittest.TestCase): 10 | 11 | def setUp(self): 12 | self.exports = kojihub.RootExports() 13 | self.get_user = mock.patch('kojihub.kojihub.get_user').start() 14 | 15 | def tearDown(self): 16 | mock.patch.stopall() 17 | 18 | def test_non_exist_user(self): 19 | username = 'test-user' 20 | self.get_user.return_value = None 21 | with self.assertRaises(koji.GenericError) as cm: 22 | self.exports.enableUser(username) 23 | self.assertEqual("No such user: %s" % username, str(cm.exception)) 24 | -------------------------------------------------------------------------------- /tests/test_hub/test_search.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | import koji 4 | import kojihub 5 | 6 | 7 | class TestSearch(unittest.TestCase): 8 | 9 | def setUp(self): 10 | self.exports = kojihub.RootExports() 11 | 12 | def test_empty_terms(self): 13 | with self.assertRaises(koji.GenericError) as cm: 14 | self.exports.search('', 'type', 'glob') 15 | self.assertEqual("empty search terms", str(cm.exception)) 16 | 17 | def test_wrong_type(self): 18 | type = 'test-type' 19 | with self.assertRaises(koji.GenericError) as cm: 20 | self.exports.search('item', type, 'glob') 21 | self.assertEqual(f"No such search type: {type}", str(cm.exception)) 22 | -------------------------------------------------------------------------------- /tests/test_hub/test_disable_user.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from unittest import mock 4 | 5 | import koji 6 | import kojihub 7 | 8 | 9 | class TestDisableUser(unittest.TestCase): 10 | 11 | def setUp(self): 12 | self.exports = kojihub.RootExports() 13 | self.get_user = mock.patch('kojihub.kojihub.get_user').start() 14 | 15 | def tearDown(self): 16 | mock.patch.stopall() 17 | 18 | def test_non_exist_user(self): 19 | username = 'test-user' 20 | self.get_user.return_value = None 21 | with self.assertRaises(koji.GenericError) as cm: 22 | self.exports.disableUser(username) 23 | self.assertEqual("No such user: %s" % username, str(cm.exception)) 24 | -------------------------------------------------------------------------------- /tests/test_lib/data/specs/test-deps._spec: -------------------------------------------------------------------------------- 1 | Name: test-deps 2 | Version: 1 3 | Release: 1%{?dist} 4 | Summary: Testing dependency header fields 5 | 6 | License: none 7 | 8 | Requires: require1 9 | Requires: require2 10 | Provides: provide1 11 | Provides: provide2 12 | Obsoletes: obsoletes1 13 | Obsoletes: obsoletes2 14 | Conflicts: conflicts1 15 | Conflicts: conflicts2 16 | Suggests: suggests1 17 | Suggests: suggests2 18 | Enhances: enhances1 19 | Enhances: enhances2 20 | Supplements: supplements1 21 | Supplements: supplements2 22 | Recommends: recommends1 23 | Recommends: recommends2 24 | 25 | %description 26 | Testing dependency header fields 27 | 28 | %files 29 | -------------------------------------------------------------------------------- /tests/test_hub/test_get_win_archive.py: -------------------------------------------------------------------------------- 1 | import kojihub 2 | from .utils import DBQueryTestCase 3 | 4 | 5 | class TestGetWinArchive(DBQueryTestCase): 6 | 7 | def setUp(self): 8 | super(TestGetWinArchive, self).setUp() 9 | self.maxDiff = None 10 | 11 | def test_valid(self): 12 | kojihub.get_win_archive(123) 13 | self.assertEqual(len(self.queries), 1) 14 | query = self.queries[0] 15 | self.assertEqual(query.tables, ['win_archives']) 16 | self.assertEqual(query.joins, None) 17 | self.assertEqual(query.clauses, ['archive_id = %(archive_id)i']) 18 | self.assertEqual(query.values, {'archive_id': 123}) 19 | self.assertEqual(query.columns, ['archive_id', 'flags', 'platforms', 'relpath']) 20 | -------------------------------------------------------------------------------- /tests/test_hub/test_get_maven_archive.py: -------------------------------------------------------------------------------- 1 | import kojihub 2 | from .utils import DBQueryTestCase 3 | 4 | 5 | class TestGetMavenArchive(DBQueryTestCase): 6 | 7 | def setUp(self): 8 | super(TestGetMavenArchive, self).setUp() 9 | self.maxDiff = None 10 | 11 | def test_valid(self): 12 | kojihub.get_maven_archive(123) 13 | self.assertEqual(len(self.queries), 1) 14 | query = self.queries[0] 15 | self.assertEqual(query.tables, ['maven_archives']) 16 | self.assertEqual(query.joins, None) 17 | self.assertEqual(query.clauses, ['archive_id = %(archive_id)i']) 18 | self.assertEqual(query.values, {'archive_id': 123}) 19 | self.assertEqual(query.columns, ['archive_id', 'artifact_id', 'group_id', 'version']) 20 | -------------------------------------------------------------------------------- /www/static/images/funnel.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /docs/source/migrations/migrating_to_1.20.rst: -------------------------------------------------------------------------------- 1 | Migrating to Koji 1.20 2 | ====================== 3 | 4 | You should consider the following changes when migrating to 1.20: 5 | 6 | DB Updates 7 | ---------- 8 | 9 | There are no big database schema changes in this release. There is only cleanup 10 | of potentially not `dropped old constraint `_. 11 | 12 | As in previous releases, we provide a migration script that updates the 13 | database. 14 | 15 | :: 16 | 17 | # psql koji koji < /usr/share/doc/koji/docs/schema-upgrade-1.19-1.20.sql 18 | 19 | 20 | Other changes 21 | ------------- 22 | 23 | There are numerous other changes in 1.20 that should not have a direct impact on 24 | migration. For details see: :doc:`../release_notes/release_notes_1.20` 25 | -------------------------------------------------------------------------------- /docs/source/migrations/migrating_to_1.25.rst: -------------------------------------------------------------------------------- 1 | Migrating to Koji 1.25 2 | ====================== 3 | 4 | You should consider the following changes when migrating to 1.25: 5 | 6 | DB Updates 7 | ---------- 8 | 9 | This release includes one schema change. 10 | 11 | Each repo now contains a link to the task which was used to create it (`Issue #888 12 | `_) 13 | 14 | 15 | As in previous releases, we provide a migration script that updates the database. 16 | 17 | :: 18 | 19 | # psql koji koji < /usr/share/doc/koji/docs/schema-upgrade-1.24-1.25.sql 20 | 21 | 22 | Other changes 23 | ------------- 24 | 25 | There are numerous other changes in 1.25 that should not have a direct impact on migration. For 26 | details see: :doc:`../release_notes/release_notes_1.25` 27 | -------------------------------------------------------------------------------- /schemas/schema-upgrade-1.30-1.31.sql: -------------------------------------------------------------------------------- 1 | -- upgrade script to migrate the Koji database schema 2 | -- from version 1.30 to 1.31 3 | 4 | BEGIN; 5 | -- index for default search method for rpms, PG11+ can benefit from new include method 6 | DO $$ 7 | DECLARE version integer; 8 | BEGIN 9 | SELECT current_setting('server_version_num')::integer INTO version; 10 | IF version >= 110000 THEN 11 | EXECUTE 'CREATE INDEX rpminfo_filename ON rpminfo((name || ''-'' || version || ''-'' || release || ''.'' || arch || ''.rpm'')) INCLUDE (id);'; 12 | ELSE 13 | EXECUTE 'CREATE INDEX rpminfo_filename ON rpminfo((name || ''-'' || version || ''-'' || release || ''.'' || arch || ''.rpm''));'; 14 | END IF; 15 | END 16 | $$; 17 | COMMIT; 18 | -------------------------------------------------------------------------------- /tests/test_cli/test_unique_path.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | import unittest 3 | import sys 4 | 5 | from six.moves import range 6 | 7 | from koji_cli.lib import unique_path 8 | 9 | 10 | class TestUniquePath(unittest.TestCase): 11 | 12 | def test_unique_path(self): 13 | for i in range(1000): 14 | self.assertNotEqual(unique_path('prefix'), unique_path('prefix')) 15 | if sys.version_info >= (3, 2): 16 | return self.assertRegex( 17 | unique_path('prefix'), r'^prefix/\d{10}\.\d{1,7}\.[a-zA-Z]{8}$') 18 | else: 19 | return self.assertRegexpMatches( 20 | unique_path('prefix'), r'^prefix/\d{10}\.\d{1,7}\.[a-zA-Z]{8}$') 21 | 22 | 23 | if __name__ == '__main__': 24 | unittest.main() 25 | -------------------------------------------------------------------------------- /tests/test_hub/data/maven/import_1/com/mycompany/app/my-app/1.0-SNAPSHOT/my-app-1.0-20170927.043542-1.pom: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | com.mycompany.app 5 | my-app 6 | jar 7 | 1.0-SNAPSHOT 8 | my-app 9 | http://maven.apache.org 10 | 11 | 12 | 13 | junit 14 | junit 15 | 3.8.1 16 | test 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /tests/test_hub/test_db/test_savepoint.py: -------------------------------------------------------------------------------- 1 | try: 2 | from unittest import mock 3 | except ImportError: 4 | import mock 5 | 6 | import unittest 7 | 8 | import kojihub 9 | 10 | 11 | class TestSavepoint(unittest.TestCase): 12 | 13 | def setUp(self): 14 | self.dml = mock.patch('kojihub.db._dml').start() 15 | self.context_db = mock.patch('kojihub.db.context').start() 16 | 17 | def tearDown(self): 18 | mock.patch.stopall() 19 | 20 | def test_savepoint(self): 21 | sp = kojihub.Savepoint('some_name') 22 | self.assertEqual(sp.name, 'some_name') 23 | self.dml.assert_called_once_with('SAVEPOINT some_name', {}) 24 | 25 | self.dml.reset_mock() 26 | sp.rollback() 27 | self.dml.assert_called_once_with('ROLLBACK TO SAVEPOINT some_name', {}) 28 | -------------------------------------------------------------------------------- /tests/test_kojira/loadkojira.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | import os 3 | import sys 4 | 5 | try: 6 | import importlib 7 | import importlib.machinery 8 | except ImportError: 9 | import imp 10 | importlib = None 11 | 12 | # TODO - libify kojira so we don't need this hack 13 | KOJIRA_MOD = "kojira_" 14 | KOJIRA_FILENAME = os.path.dirname(__file__) + "/../../util/kojira" 15 | 16 | if importlib: 17 | importlib.machinery.SOURCE_SUFFIXES.append('') 18 | spec = importlib.util.spec_from_file_location(KOJIRA_MOD, KOJIRA_FILENAME) 19 | kojira = importlib.util.module_from_spec(spec) 20 | sys.modules[KOJIRA_MOD] = kojira 21 | spec.loader.exec_module(kojira) 22 | importlib.machinery.SOURCE_SUFFIXES.pop() 23 | else: 24 | kojira = imp.load_source(KOJIRA_MOD, KOJIRA_FILENAME) 25 | -------------------------------------------------------------------------------- /www/lib/kojiweb/Makefile: -------------------------------------------------------------------------------- 1 | PACKAGE = $(shell basename `pwd`) 2 | PYFILES = $(wildcard *.py) 3 | PKGDIR = $(shell $(PYTHON) ../../devtools/get_site_packages.py )/$(PACKAGE) 4 | 5 | SERVERDIR = /kojiweb 6 | FILES = $(wildcard *.py *.html.j2) 7 | 8 | _default: 9 | @echo "nothing to make. try make install" 10 | 11 | clean: 12 | rm -f *.o *.so *.pyc *~ 13 | rm -rf __pycache__ 14 | for d in $(SUBDIRS); do make -s -C $$d clean; done 15 | 16 | install: 17 | @if [ "$(DESTDIR)" = "" ]; then \ 18 | echo " "; \ 19 | echo "ERROR: A destdir is required"; \ 20 | exit 1; \ 21 | fi 22 | 23 | mkdir -p $(DESTDIR)/$(SERVERDIR) 24 | for p in $(PYFILES) ; do \ 25 | install -p -m 644 $$p $(DESTDIR)/$(SERVERDIR)/$$p; \ 26 | done 27 | $(PYTHON) -c "import compileall; compileall.compile_dir('$(DESTDIR)/$(SERVERDIR)', 1, '$(PYDIR)', 1)" 28 | -------------------------------------------------------------------------------- /tests/test_lib/test_base64.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | from __future__ import absolute_import 3 | import six 4 | import unittest 5 | 6 | from koji.util import base64encode 7 | 8 | 9 | class Base64EncodeTestCase(unittest.TestCase): 10 | 11 | DATA = [ 12 | # list of pairs [string, encoded_string] 13 | [b'Hello World', 'SGVsbG8gV29ybGQ='], 14 | [b'BZh91AY&SY\x14\x99\\\xcf\x05y\r\x7f\xff\xff', 15 | 'QlpoOTFBWSZTWRSZXM8FeQ1///8='] 16 | ] 17 | 18 | def test_base64encode(self): 19 | for s, expected in self.DATA: 20 | result = base64encode(s) 21 | self.assertEqual(result, expected) 22 | if six.PY3: 23 | result = base64encode(s, as_bytes=True) 24 | self.assertEqual(result, expected.encode('ascii')) 25 | -------------------------------------------------------------------------------- /docs/source/migrations/migrating_to_1.24.rst: -------------------------------------------------------------------------------- 1 | Migrating to Koji 1.24 2 | ====================== 3 | 4 | You should consider the following changes when migrating to 1.24: 5 | 6 | DB Updates 7 | ---------- 8 | 9 | This release includes one minor schema change. 10 | 11 | As we now can have architectures defined for individual external repos, we need 12 | to reflect it in db. (see `PR#2564 13 | `_). 14 | 15 | As in previous releases, we provide a migration script that updates the database. 16 | 17 | :: 18 | 19 | # psql koji koji < /usr/share/doc/koji/docs/schema-upgrade-1.23-1.24.sql 20 | 21 | 22 | Other changes 23 | ------------- 24 | 25 | There are numerous other changes in 1.24 that should not have a direct impact on migration. For 26 | details see: :doc:`../release_notes/release_notes_1.24` 27 | -------------------------------------------------------------------------------- /www/kojiweb/templates/reports.html.j2: -------------------------------------------------------------------------------- 1 | #include "header.html.j2" 2 | 3 |

Reports

4 | 5 | 18 | 19 | #include "footer.html.j2" 20 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Koji - a system for building and tracking software. 2 | Copyright (c) 2007-2014 Red Hat, Inc. 3 | 4 | Koji is free software; you can redistribute it and/or 5 | modify it under the terms of the GNU Lesser General Public 6 | License as published by the Free Software Foundation; 7 | version 2.1 of the License. 8 | 9 | This software is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | Lesser General Public License for more details. 13 | 14 | You should have received a copy of the GNU Lesser General Public 15 | License along with this software; if not, write to the Free Software 16 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 | -------------------------------------------------------------------------------- /www/kojiweb/templates/externalrepoinfo.html.j2: -------------------------------------------------------------------------------- 1 | #include "header.html.j2" 2 | 3 |

Information for external repo {{ extRepo.name }}

4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 26 | 27 |
Name{{ extRepo.name }}
ID{{ extRepo.id }}
URL{{ extRepo.url }}
Tags using this external repo 18 | #if repoTags|length 19 | #for tag in repoTags 20 | {{ tag.tag_name }}
21 | #endfor 22 | #else 23 | No tags 24 | #endif 25 |
28 | 29 | #include "footer.html.j2" 30 | -------------------------------------------------------------------------------- /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | select = E,F,W,C,I 3 | ignore = 4 | # too many leading ‘#’ for block comment 5 | E266, 6 | # do not assign a lambda expression, use a def 7 | E731, 8 | # [PY2] list comprehension redefines `name` from line `N` 9 | F812, 10 | # line break after binary operator 11 | W504 12 | max_line_length = 99 13 | exclude = 14 | .git, 15 | __pycache__, 16 | tests, 17 | docs, 18 | ./koji-*/*, 19 | .tox 20 | 21 | filename = 22 | *.py, 23 | ./cli/koji, 24 | ./builder/kojid, 25 | ./builder/mergerepos, 26 | ./hub/rpmdiff, 27 | ./util/kojira, 28 | ./util/koji-gc, 29 | ./util/koji-shadow, 30 | ./util/koji-sweep-db, 31 | ./util/koji-sidetag-cleanup, 32 | ./vm/kojivmd 33 | 34 | application_import_names = koji,koji_cli,kojihub,kojiweb,__main__ 35 | import_order_style = pep8 36 | -------------------------------------------------------------------------------- /cli/Makefile: -------------------------------------------------------------------------------- 1 | SUBDIRS=koji_cli 2 | 3 | PACKAGE = $(shell basename `pwd`) 4 | PKGDIR = $(shell $(PYTHON) ../devtools/get_site_packages.py )/$(PACKAGE) 5 | 6 | FILES = koji 7 | 8 | _default: 9 | @echo "nothing to make. try make install" 10 | 11 | clean: 12 | rm -f *.o *.so *.pyc *~ kojic 13 | rm -rf __pycache__ 14 | for d in $(SUBDIRS); do make -s -C $$d clean; done 15 | 16 | install: 17 | @if [ "$(DESTDIR)" = "" ]; then \ 18 | echo " "; \ 19 | echo "ERROR: A destdir is required"; \ 20 | exit 1; \ 21 | fi 22 | 23 | for d in $(SUBDIRS); do make DESTDIR=$(DESTDIR) \ 24 | -C $$d install; [ $$? = 0 ] || exit 1; done 25 | 26 | mkdir -p $(DESTDIR)/usr/bin 27 | install -p -m 755 $(FILES) $(DESTDIR)/usr/bin 28 | mkdir -p $(DESTDIR)/etc/koji.conf.d 29 | install -p -m 644 koji.conf $(DESTDIR)/etc/koji.conf 30 | install -p -m 644 koji.conf $(DESTDIR)/etc/koji.conf 31 | -------------------------------------------------------------------------------- /docs/source/migrations/migrating_to_1.17.rst: -------------------------------------------------------------------------------- 1 | Migrating to Koji 1.17 2 | ====================== 3 | 4 | .. 5 | reStructured Text formatted 6 | 7 | You should consider the following changes when migrating to 1.17: 8 | 9 | DB Updates 10 | ---------- 11 | 12 | This release some minor schema changes 13 | 14 | * the ``tag_external_repos`` table has a new ``merge_mode`` column 15 | * the ``build_target.name`` column is now a TEXT field rather than VARCHAR 16 | 17 | As in previous releases, we provide a migration script that updates the 18 | database. 19 | 20 | :: 21 | 22 | # psql koji koji `_, `#1849 `_ 13 | `#1851 `_) 14 | 15 | 16 | As in previous releases, we provide a migration script that updates the database. 17 | 18 | :: 19 | 20 | # psql koji koji < /usr/share/doc/koji/docs/schema-upgrade-1.25-1.26.sql 21 | 22 | 23 | Other changes 24 | ------------- 25 | 26 | There are numerous other changes in 1.26 that should not have a direct impact on migration. For 27 | details see: :doc:`../release_notes/release_notes_1.26` 28 | -------------------------------------------------------------------------------- /docs/source/migrations/migrations.rst: -------------------------------------------------------------------------------- 1 | ========== 2 | Migrations 3 | ========== 4 | 5 | .. toctree:: 6 | :maxdepth: 1 7 | 8 | migrating_to_1.35 9 | migrating_to_1.34 10 | migrating_to_1.33 11 | migrating_to_1.32 12 | migrating_to_1.31 13 | migrating_to_1.30 14 | migrating_to_1.29 15 | migrating_to_1.28 16 | migrating_to_1.27 17 | migrating_to_1.26 18 | migrating_to_1.25 19 | migrating_to_1.24 20 | migrating_to_1.23 21 | migrating_to_1.22 22 | migrating_to_1.21 23 | migrating_to_1.20 24 | migrating_to_1.19 25 | migrating_to_1.18 26 | migrating_to_1.17 27 | migrating_to_1.16 28 | migrating_to_1.15 29 | migrating_to_1.14 30 | migrating_to_1.13 31 | migrating_to_1.12 32 | migrating_to_1.11 33 | migrating_to_1.10 34 | migrating_to_1.9 35 | migrating_to_1.8 36 | migrating_to_1.7 37 | -------------------------------------------------------------------------------- /schemas/schema-upgrade-1.6-1.7.sql: -------------------------------------------------------------------------------- 1 | BEGIN; 2 | 3 | CREATE TABLE volume ( 4 | id SERIAL NOT NULL PRIMARY KEY, 5 | name TEXT UNIQUE NOT NULL 6 | ) WITHOUT OIDS; 7 | 8 | INSERT INTO volume (id, name) VALUES (0, 'DEFAULT'); 9 | 10 | ALTER TABLE build ADD COLUMN volume_id INTEGER REFERENCES volume (id); 11 | UPDATE build SET volume_id = 0; 12 | ALTER TABLE build ALTER COLUMN volume_id SET NOT NULL; 13 | 14 | CREATE TABLE tag_updates ( 15 | id SERIAL NOT NULL PRIMARY KEY, 16 | tag_id INTEGER NOT NULL REFERENCES tag(id), 17 | update_event INTEGER NOT NULL REFERENCES events(id) DEFAULT get_event(), 18 | updater_id INTEGER NOT NULL REFERENCES users(id), 19 | update_type INTEGER NOT NULL 20 | ) WITHOUT OIDS; 21 | 22 | CREATE INDEX tag_updates_by_tag ON tag_updates (tag_id); 23 | CREATE INDEX tag_updates_by_event ON tag_updates (update_event); 24 | 25 | COMMIT; 26 | -------------------------------------------------------------------------------- /tests/test_cli/test_arg_filter.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from koji_cli.lib import arg_filter 4 | 5 | class TestArgFilter(unittest.TestCase): 6 | def test_valid_values(self): 7 | for parse_json in (True, False): 8 | self.assertEqual(arg_filter("1", parse_json=parse_json), 1) 9 | self.assertEqual(arg_filter("1.123", parse_json=parse_json), 1.123) 10 | self.assertEqual(arg_filter("True", parse_json=parse_json), True) 11 | self.assertEqual(arg_filter("False", parse_json=parse_json), False) 12 | self.assertEqual(arg_filter("None", parse_json=parse_json), None) 13 | 14 | # non/json 15 | self.assertEqual(arg_filter('{"a": 1}'), '{"a": 1}') 16 | self.assertDictEqual(arg_filter('{"a": 1}', parse_json=True), {"a": 1}) 17 | 18 | # invalid json 19 | self.assertEqual(arg_filter("{'a': 1}", parse_json=True), "{'a': 1}") 20 | -------------------------------------------------------------------------------- /tests/test_hub/test_import_archive_internal.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from unittest import mock 3 | 4 | import koji 5 | import kojihub 6 | 7 | 8 | class TestImportArchiveInternal(unittest.TestCase): 9 | def setUp(self): 10 | self.os_path_exists = mock.patch('os.path.exists').start() 11 | 12 | def tearDown(self): 13 | mock.patch.stopall() 14 | 15 | def test_import_archive_internal_non_exist_filepath(self): 16 | self.os_path_exists.return_value = False 17 | filepath = 'test/file/path/to/archive' 18 | buildinfo = {'id': 1, 'name': 'test-build'} 19 | type_archive = 'maven' 20 | typeInfo = {'group_id': 1, 'artifact_id': 2, 'version': 3} 21 | with self.assertRaises(koji.GenericError) as cm: 22 | kojihub.import_archive_internal(filepath, buildinfo, type_archive, typeInfo) 23 | self.assertEqual(f"No such file: {filepath}", str(cm.exception)) 24 | -------------------------------------------------------------------------------- /docs/source/migrations/migrating_to_1.21.rst: -------------------------------------------------------------------------------- 1 | Migrating to Koji 1.21 2 | ====================== 3 | 4 | You should consider the following changes when migrating to 1.21: 5 | 6 | DB Updates 7 | ---------- 8 | 9 | There are no big database schema changes in this release. 10 | 11 | We've updated table with events and ``get_event()`` function to better handle 12 | timeline (`PR#2068 `_) and set default 13 | merge mode for external repos (`PR#2051 `_) 14 | 15 | As in previous releases, we provide a migration script that updates the 16 | database. 17 | 18 | :: 19 | 20 | # psql koji koji < /usr/share/doc/koji/docs/schema-upgrade-1.20-1.21.sql 21 | 22 | 23 | Other changes 24 | ------------- 25 | 26 | There are numerous other changes in 1.21 that should not have a direct impact on 27 | migration. For details see: :doc:`../release_notes/release_notes_1.21` 28 | -------------------------------------------------------------------------------- /docs/source/supported_platforms.rst: -------------------------------------------------------------------------------- 1 | Supported Platforms 2 | =================== 3 | 4 | We're now supporting Linux systems which have at least python 2.7 for 5 | builders and 3.6 for other components. These versions are minimal (so, 6 | everywhere where is 2.7 support it means 2.7+ *and* 3.0+. Currently it 7 | involves all active Fedoras and RHEL/CentOS 7+. 8 | 9 | +-----------+-----+-----+---------+-------+-----+-----+ 10 | | Component | Hub | Web | Builder | Utils | Lib | CLI | 11 | +===========+=====+=====+=========+=======+=====+=====+ 12 | | Python | 3.6 | 3.6 | 2.7 | 3.6 | 2.7 | 2.7 | 13 | +-----------+-----+-----+---------+-------+-----+-----+ 14 | 15 | For database we're supporting RHEL/CentOS 8+. So, it means that 16 | postgresl 10 is still supported, anyway we encourage using at 17 | least PG 12 (``dnf module enable postgresql:12``). At least some 18 | indices are set up in more efficient way with newer PG 19 | capabilities. 20 | -------------------------------------------------------------------------------- /tests/test_hub/test_write_signed_rpm.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from unittest import mock 3 | 4 | import koji 5 | import kojihub 6 | 7 | 8 | class TestWriteSignedRPM(unittest.TestCase): 9 | def setUp(self): 10 | self.context = mock.patch('kojihub.kojihub.context').start() 11 | self.get_rpm = mock.patch('kojihub.kojihub.get_rpm').start() 12 | 13 | def tearDown(self): 14 | mock.patch.stopall() 15 | 16 | def test_write_signed_rpm_not_internal_rpm(self): 17 | sigkey = 'test-sigkey' 18 | rpm_id = 1 19 | rpminfo = {'external_repo_id': 1, 'external_repo_name': 'test-external-repo'} 20 | self.get_rpm.return_value = rpminfo 21 | with self.assertRaises(koji.GenericError) as cm: 22 | kojihub.write_signed_rpm(rpm_id, sigkey) 23 | self.assertEqual(f"Not an internal rpm: {rpm_id} (from {rpminfo['external_repo_name']})", 24 | str(cm.exception)) 25 | -------------------------------------------------------------------------------- /tests/test_hub/data/maven/import_1/com/mycompany/app/my-app/1.0-SNAPSHOT/maven-metadata.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | com.mycompany.app 4 | my-app 5 | 1.0-SNAPSHOT 6 | 7 | 8 | 20170927.043542 9 | 1 10 | 11 | 20170927043542 12 | 13 | 14 | jar 15 | 1.0-20170927.043542-1 16 | 20170927043542 17 | 18 | 19 | pom 20 | 1.0-20170927.043542-1 21 | 20170927043542 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /docs/source/migrations/migrating_to_1.16.rst: -------------------------------------------------------------------------------- 1 | Migrating to Koji 1.16 2 | ====================== 3 | 4 | .. 5 | reStructured Text formatted 6 | 7 | You should consider the following changes when migrating to 1.16: 8 | 9 | DB Updates 10 | ---------- 11 | 12 | This release has schema changes to support tracking history for hosts. 13 | 14 | * new table: ``host_config`` 15 | * some fields from the ``host`` table have moved to ``host_config`` 16 | * the ``host_channels`` table now has versioning data like the other 17 | versioned tables 18 | 19 | As in previous releases, we provide a migration script that updates the 20 | database. 21 | 22 | :: 23 | 24 | # psql koji koji Information for target {{ target.name }} 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | #if "admin" in perms 19 | 20 | 21 | 22 | 23 | 24 | 25 | #endif 26 |
Name{{ target.name }}
ID{{ target.id }}
Build Tag{{ buildTag.name }}
Destination Tag{{ destTag.name }}
Edit
Delete
27 | 28 | #include "footer.html.j2" 29 | -------------------------------------------------------------------------------- /schemas/schema-upgrade-1.32-1.33.sql: -------------------------------------------------------------------------------- 1 | -- upgrade script to migrate the Koji database schema 2 | -- from version 1.32 to 1.33 3 | 4 | BEGIN; 5 | ALTER TABLE sessions ADD COLUMN renew_time TIMESTAMPTZ; 6 | INSERT INTO archivetypes (name, description, extensions) VALUES ('checksum', 'Checksum file', 'sha256') ON CONFLICT DO NOTHING; 7 | INSERT INTO archivetypes (name, description, extensions) VALUES ('changes', 'Kiwi changes file', 'changes.xz changes') ON CONFLICT DO NOTHING; 8 | INSERT INTO archivetypes (name, description, extensions) VALUES ('packages', 'Kiwi packages listing', 'packages') ON CONFLICT DO NOTHING; 9 | INSERT INTO archivetypes (name, description, extensions) VALUES ('verified', 'Kiwi verified package list', 'verified') ON CONFLICT DO NOTHING; 10 | ALTER TABLE host ADD COLUMN update_time TIMESTAMPTZ; 11 | CREATE TABLE locks ( 12 | name TEXT NOT NULL PRIMARY KEY 13 | ) WITHOUT OIDS; 14 | INSERT INTO locks(name) VALUES('protonmsg-plugin'); 15 | COMMIT; 16 | -------------------------------------------------------------------------------- /tests/test_hub/test_perm_operations.py: -------------------------------------------------------------------------------- 1 | 2 | from unittest import mock 3 | 4 | import unittest 5 | 6 | import koji 7 | import kojihub 8 | 9 | 10 | class TestPerm(unittest.TestCase): 11 | 12 | @mock.patch('kojihub.kojihub.context') 13 | @mock.patch('kojihub.kojihub.lookup_perm', return_value=None) 14 | def test_has_perm(self, lookup_perm, context): 15 | rv = kojihub.RootExports().hasPerm('perm') 16 | self.assertEqual(rv, context.session.hasPerm.return_value) 17 | lookup_perm.assert_not_called() 18 | context.session.hasPerm.assert_called_once_with('perm') 19 | 20 | context.session.hasPerm.reset_mock() 21 | with self.assertRaises(koji.GenericError) as cm: 22 | kojihub.RootExports().hasPerm('perm', strict=True) 23 | lookup_perm.assert_called_once_with('perm') 24 | context.session.hasPerm.assert_not_called() 25 | self.assertEqual(cm.exception.args[0], 26 | 'No such permission perm defined') 27 | -------------------------------------------------------------------------------- /docs/source/release_notes/release_notes_1.19.1.rst: -------------------------------------------------------------------------------- 1 | Koji 1.19.1 Release notes 2 | ========================= 3 | 4 | This is a small bugfix release for significant 1.19 bugs. 5 | 6 | Client Changes 7 | -------------- 8 | 9 | **Fix permissions to check tag/target/host permissions** 10 | 11 | | PR: https://pagure.io/koji/pull-request/1733 12 | 13 | In previous release the tag/target/host permissions were not being properly checked 14 | by the client, this change includes those checks. 15 | 16 | 17 | 18 | System Changes 19 | -------------- 20 | 21 | **Fix hub reporting of incorrect ownership data** 22 | 23 | | PR: https://pagure.io/koji/pull-request/1753 24 | 25 | This change fixes package owner listing; in previous release, information returned by ``list-pkgs`` 26 | was incorrect. 27 | 28 | 29 | **Fix issue with listing users with old versions of Postgres** 30 | 31 | | PR: https://pagure.io/koji/pull-request/1751 32 | 33 | ``array_remove`` was removed and replaced to support Postgres versions older than 9.4. 34 | -------------------------------------------------------------------------------- /vm/Makefile: -------------------------------------------------------------------------------- 1 | BINFILES = kojivmd 2 | SHAREFILES = kojikamid 3 | SBINDIR ?= /usr/sbin 4 | SYSTEMDSYSTEMUNITDIR = $(shell pkg-config systemd --variable=systemdsystemunitdir) 5 | 6 | _default: 7 | @echo "nothing to make. try make install" 8 | 9 | clean: 10 | rm -f *.o *.so *.pyc *~ kojikamid kojikamidc kojivmdc 11 | 12 | kojikamid: kojikamid.py 13 | bash fix_kojikamid.sh >kojikamid 14 | 15 | _install: kojikamid 16 | @if [ "$(DESTDIR)" = "" ]; then \ 17 | echo " "; \ 18 | echo "ERROR: A destdir is required"; \ 19 | exit 1; \ 20 | fi 21 | 22 | mkdir -p $(DESTDIR)$(SBINDIR) 23 | install -p -m 755 $(BINFILES) $(DESTDIR)$(SBINDIR) 24 | 25 | mkdir -p $(DESTDIR)/usr/share/kojivmd 26 | install -p -m 644 $(SHAREFILES) $(DESTDIR)/usr/share/kojivmd 27 | 28 | mkdir -p $(DESTDIR)/etc/kojivmd 29 | install -p -m 644 kojivmd.conf $(DESTDIR)/etc/kojivmd/kojivmd.conf 30 | 31 | install: _install 32 | mkdir -p $(DESTDIR)$(SYSTEMDSYSTEMUNITDIR) 33 | install -p -m 644 kojivmd.service $(DESTDIR)$(SYSTEMDSYSTEMUNITDIR) 34 | -------------------------------------------------------------------------------- /docs/source/migrations/migrating_to_1.19.rst: -------------------------------------------------------------------------------- 1 | Migrating to Koji 1.19 2 | ====================== 3 | 4 | .. 5 | reStructured Text formatted 6 | 7 | You should consider the following changes when migrating to 1.19: 8 | 9 | DB Updates 10 | ---------- 11 | 12 | This release has a few schema changes: 13 | 14 | * A new ``tag_package_owners`` table 15 | * A new ``user_krb_principals`` table 16 | * Updates to the data in the ``archivetypes`` table 17 | * Updates to the data in the ``permissions`` table 18 | * The ``content_generator`` table now enforces unique strings in the ``names`` field 19 | 20 | As in previous releases, we provide a migration script that updates the 21 | database. 22 | 23 | :: 24 | 25 | # psql koji koji `_. 6 | Most important changes are listed here. 7 | 8 | 9 | Migrating from Koji 1.35.0 10 | -------------------------- 11 | 12 | No special actions are needed. 13 | 14 | 15 | Security Fixes 16 | -------------- 17 | 18 | **web: XSS vulnerability** 19 | 20 | | CVE: :doc:`../CVEs/CVE-2024-9427` 21 | | Issue: https://pagure.io/koji/issue/4204 22 | 23 | An unsanitized input allows for an XSS attack. Javascript code from a malicious 24 | link could be reflected in the resulting web page. At present, we do not 25 | believe that this can be used to submit an action or make a change in Koji due 26 | to existing XSS protections in the code. Even so, this is a serious issue and 27 | we recommend applying this update promptly. 28 | 29 | 30 | Other Changes 31 | ------------- 32 | 33 | There are no other significant changes in this release. 34 | All changes can be found in `the roadmap `_. 35 | -------------------------------------------------------------------------------- /tests/test_www/test_buildinfo.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import koji 3 | 4 | from unittest import mock 5 | from .loadwebindex import webidx 6 | 7 | 8 | class TestBuildInfo(unittest.TestCase): 9 | def setUp(self): 10 | self.get_server = mock.patch.object(webidx, "_getServer").start() 11 | self.server = mock.MagicMock() 12 | self.environ = { 13 | 'koji.options': { 14 | 'SiteName': 'test', 15 | 'KojiFilesURL': 'https://server.local/files', 16 | }, 17 | 'koji.currentUser': None 18 | } 19 | self.build_id = '1' 20 | 21 | def tearDown(self): 22 | mock.patch.stopall() 23 | 24 | def test_buildinfo_exception(self): 25 | """Test taskinfo function raises exception""" 26 | self.server.getBuild.side_effect = koji.GenericError 27 | self.get_server.return_value = self.server 28 | 29 | with self.assertRaises(koji.GenericError) as cm: 30 | webidx.buildinfo(self.environ, self.build_id) 31 | self.assertEqual(str(cm.exception), f'No such build ID: {self.build_id}') 32 | -------------------------------------------------------------------------------- /docs/source/release_notes/release_notes_1.33.2.rst: -------------------------------------------------------------------------------- 1 | 2 | Koji 1.33.2 Release notes 3 | ========================= 4 | 5 | This is a security update to backport the fix for :doc:`../CVEs/CVE-2024-9427` 6 | to Koji 1.33. 7 | 8 | 9 | Migrating from Koji 1.33.x 10 | -------------------------- 11 | 12 | No special actions are needed to migrate from earlier 1.33 point releases. 13 | 14 | 15 | Security Fixes 16 | -------------- 17 | 18 | **web: XSS vulnerability** 19 | 20 | | CVE: :doc:`../CVEs/CVE-2024-9427` 21 | | Issue: https://pagure.io/koji/issue/4212 22 | 23 | An unsanitized input allows for an XSS attack. Javascript code from a malicious 24 | link could be reflected in the resulting web page. At present, we do not 25 | believe that this can be used to submit an action or make a change in Koji due 26 | to existing XSS protections in the code. Even so, this is a serious issue and 27 | we recommend applying this update promptly. 28 | 29 | 30 | Other Changes 31 | ------------- 32 | 33 | There are no other significant changes in this release. 34 | All changes can be found in `the roadmap `_. 35 | -------------------------------------------------------------------------------- /docs/source/release_notes/release_notes_1.34.3.rst: -------------------------------------------------------------------------------- 1 | 2 | Koji 1.34.3 Release notes 3 | ========================= 4 | 5 | This is a security update to backport the fix for :doc:`../CVEs/CVE-2024-9427` 6 | to Koji 1.34. 7 | 8 | 9 | Migrating from Koji 1.34.x 10 | -------------------------- 11 | 12 | No special actions are needed to migrate from earlier 1.34 point releases. 13 | 14 | 15 | Security Fixes 16 | -------------- 17 | 18 | **web: XSS vulnerability** 19 | 20 | | CVE: :doc:`../CVEs/CVE-2024-9427` 21 | | Issue: https://pagure.io/koji/issue/4211 22 | 23 | An unsanitized input allows for an XSS attack. Javascript code from a malicious 24 | link could be reflected in the resulting web page. At present, we do not 25 | believe that this can be used to submit an action or make a change in Koji due 26 | to existing XSS protections in the code. Even so, this is a serious issue and 27 | we recommend applying this update promptly. 28 | 29 | 30 | Other Changes 31 | ------------- 32 | 33 | There are no other significant changes in this release. 34 | All changes can be found in `the roadmap `_. 35 | -------------------------------------------------------------------------------- /tests/test_lib/data/maven/config.ini: -------------------------------------------------------------------------------- 1 | [pkg1] 2 | scmurl=scmurl 3 | patches=patchurl 4 | specfile=specfile 5 | goals=goal1 goal2 6 | profiles=profile1 profile2 7 | packages=pkg1 pkg2 8 | jvm_options=--opt1 --opt2=val 9 | maven_options=--opt1 --opt2=val 10 | properties: p1=1 11 | p2 12 | p3=ppp3 13 | envs:e1=1 14 | e2=2 15 | buildrequires=r1 r2 16 | otheropts=others 17 | 18 | [pkg2] 19 | type=maven 20 | scmurl=scmurl 21 | patches=patchurl 22 | specfile=specfile 23 | goals=goal1 goal2 24 | profiles=profile1 profile2 25 | packages=pkg1 pkg2 26 | jvm_options=--opt1 --opt2=val 27 | maven_options=--opt1 --opt2=val 28 | properties: p1=1 29 | p2 30 | p3=ppp3 31 | envs:e1=1 32 | e2=2 33 | buildrequires=r1 r2 34 | otheropts=others 35 | 36 | [pkg3] 37 | type=wrapper 38 | scmurl=scmurl 39 | patches=patchurl 40 | specfile=specfile 41 | goals=goal1 goal2 42 | profiles=profile1 profile2 43 | packages=pkg1 pkg2 44 | jvm_options=--opt1 --opt2=val 45 | maven_options=--opt1 --opt2=val 46 | properties: p1=1 47 | p2 48 | p3=ppp3 49 | envs:e1=1 50 | e2=2 51 | buildrequires=r1 52 | otheropts=others 53 | -------------------------------------------------------------------------------- /tests/test_hub/test_get_task_children.py: -------------------------------------------------------------------------------- 1 | from unittest import mock 2 | 3 | import koji 4 | import kojihub 5 | from .utils import DBQueryTestCase 6 | 7 | QP = kojihub.QueryProcessor 8 | 9 | 10 | class TestGetTaskChildren(DBQueryTestCase): 11 | def setUp(self): 12 | super(TestGetTaskChildren, self).setUp() 13 | self.exports = kojihub.RootExports() 14 | 15 | def tearDown(self): 16 | mock.patch.stopall() 17 | 18 | def test_get_task_children_non_existing(self): 19 | self.qp_execute_return_value = [] 20 | 21 | r = self.exports.getTaskChildren(1000) 22 | 23 | self.assertEqual(r, []) 24 | 25 | def test_get_task_children_non_existing_strict(self): 26 | # get task info 27 | self.qp_execute_one_side_effect = koji.GenericError 28 | 29 | with self.assertRaises(koji.GenericError): 30 | self.exports.getTaskChildren(1000, strict=True) 31 | 32 | def test_get_task_children(self): 33 | children = [{'id': 1}] 34 | self.qp_execute_return_value = children 35 | 36 | r = self.exports.getTaskChildren(1000) 37 | 38 | self.assertEqual(r, children) 39 | -------------------------------------------------------------------------------- /tests/test_builder/data/calls/build_notif_1/params.json: -------------------------------------------------------------------------------- 1 | { 2 | "recipients": [ 3 | "user@example.com" 4 | ], 5 | "weburl": "https://koji.example.com/kojiweb", 6 | "build": { 7 | "task_id": 8982172, 8 | "owner_name": "user", 9 | "name": "sisu", 10 | "package_id": 11059, 11 | "state": 1, 12 | "package_name": "sisu", 13 | "creation_event_id": 9798602, 14 | "creation_time": "2015-02-18 14:50:37.471074", 15 | "owner_id": 2045, 16 | "completion_time": "2015-02-18 14:57:37.107872", 17 | "epoch": 1, 18 | "version": "0.3.0", 19 | "volume_id": 0, 20 | "release": "0.2.M1.fc23", 21 | "creation_ts": 1424271037.47107, 22 | "completion_ts": 1424271457.10787, 23 | "id": 612609, 24 | "volume_name": "DEFAULT", 25 | "nvr": "sisu-0.3.0-0.2.M1.fc23", 26 | "draft": false 27 | }, 28 | "target": { 29 | "dest_tag": 292, 30 | "build_tag": 299, 31 | "build_tag_name": "f23-build", 32 | "dest_tag_name": "f23", 33 | "id": 88, 34 | "name": "rawhide" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /tests/test_hub/data/maven/import_1/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": { 3 | "": [ 4 | "my-app-1.0-SNAPSHOT-scm-sources.zip" 5 | ], 6 | "com/mycompany/app/my-app": [ 7 | "maven-metadata.xml.md5", 8 | "maven-metadata.xml.sha1", 9 | "maven-metadata.xml" 10 | ], 11 | "com/mycompany/app/my-app/1.0-SNAPSHOT": [ 12 | "my-app-1.0-20170927.043542-1.pom.md5", 13 | "maven-metadata.xml.md5", 14 | "my-app-1.0-20170927.043542-1.pom.sha1", 15 | "maven-metadata.xml.sha1", 16 | "my-app-1.0-20170927.043542-1.jar.sha1", 17 | "maven-metadata.xml", 18 | "my-app-1.0-20170927.043542-1.jar", 19 | "my-app-1.0-20170927.043542-1.pom", 20 | "my-app-1.0-20170927.043542-1.jar.md5" 21 | ] 22 | }, 23 | "buildroot_id": 3492408, 24 | "logs": [ 25 | "checkout.log", 26 | "state.log", 27 | "build.log", 28 | "root.log" 29 | ], 30 | "maven_info": { 31 | "group_id": "com.mycompany.app", 32 | "artifact_id": "my-app", 33 | "version": "1.0-SNAPSHOT" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tests/test_hub/test_show_opts.py: -------------------------------------------------------------------------------- 1 | from unittest import mock 2 | import unittest 3 | 4 | import re 5 | import kojihub 6 | 7 | UP = kojihub.UpdateProcessor 8 | IP = kojihub.InsertProcessor 9 | 10 | 11 | class TestShowOpts(unittest.TestCase): 12 | def setUp(self): 13 | self.maxDiff = None 14 | self.context = mock.patch('kojihub.kojihub.context').start() 15 | self.context.session.assertPerm = mock.MagicMock() 16 | self.exports = kojihub.RootExports() 17 | self.context.opts = {'MaxNameLengthInternal': 15, 18 | 'RegexNameInternal.compiled': re.compile('^[A-Za-z0-9/_.+-]+$')} 19 | 20 | def tearDown(self): 21 | mock.patch.stopall() 22 | 23 | def test_as_string(self): 24 | rv = self.exports.showOpts() 25 | self.assertEqual(rv, "{'MaxNameLengthInternal': 15, " 26 | "'RegexNameInternal.compiled': re.compile('^[A-Za-z0-9/_.+-]+$')}") 27 | 28 | def test_as_dict(self): 29 | rv = self.exports.showOpts(as_string=False) 30 | self.assertEqual(rv, {'MaxNameLengthInternal': 15, 31 | 'RegexNameInternal.compiled': re.compile('^[A-Za-z0-9/_.+-]+$')}) 32 | -------------------------------------------------------------------------------- /tests/test_hub/test_restart_hosts.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from unittest import mock 4 | 5 | import koji 6 | import kojihub 7 | 8 | 9 | class TestRestartHosts(unittest.TestCase): 10 | 11 | def setUp(self): 12 | self.exports = kojihub.RootExports() 13 | self.context = mock.patch('kojihub.kojihub.context').start() 14 | self.context.session.assertPerm = mock.MagicMock() 15 | self.make_task = mock.patch('kojihub.kojihub.make_task').start() 16 | 17 | def tearDown(self): 18 | mock.patch.stopall() 19 | 20 | def test_options_is_none(self): 21 | self.make_task.return_value = 13 22 | rv = self.exports.restartHosts() 23 | self.assertEqual(rv, 13) 24 | 25 | def test_options_is_not_none(self): 26 | self.make_task.return_value = 13 27 | rv = self.exports.restartHosts(options={'opt': 'open'}) 28 | self.assertEqual(rv, 13) 29 | 30 | def test_options_wrong_type(self): 31 | options = 'test-options' 32 | with self.assertRaises(koji.ParameterError) as ex: 33 | self.exports.restartHosts(options=options) 34 | self.assertEqual(f"Invalid type of options: {type(options)}", str(ex.exception)) 35 | -------------------------------------------------------------------------------- /tests/test_lib/test_context.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | import six 3 | import time 4 | import random 5 | from six.moves import range 6 | import unittest 7 | 8 | from koji.context import context 9 | 10 | 11 | class TestContext(unittest.TestCase): 12 | 13 | def test_context(self): 14 | context.foo = 1 15 | 16 | def test(): 17 | foo = random.random() 18 | context.foo = foo 19 | time.sleep(0.5 + random.random()) 20 | print(context) 21 | self.assertEqual(context.foo, foo) 22 | context._threadclear() 23 | self.assertFalse(hasattr(context, 'foo')) 24 | 25 | for x in range(1, 10): 26 | six.moves._thread.start_new_thread(test, ()) 27 | 28 | time.sleep(0.5) 29 | for i in range(10): 30 | time.sleep(0.2 + random.random()) 31 | self.assertEqual(context.foo, 1) 32 | 33 | context.foo = 2 34 | context.bar = 3 35 | self.assertEqual(context.foo, 2) 36 | self.assertEqual(context.bar, 3) 37 | context._threadclear() 38 | self.assertFalse(hasattr(context, 'foo')) 39 | self.assertFalse(hasattr(context, 'bar')) 40 | -------------------------------------------------------------------------------- /tests/test_hub/test_get_build_notifications.py: -------------------------------------------------------------------------------- 1 | from unittest import mock 2 | import unittest 3 | import koji 4 | import kojihub 5 | 6 | 7 | class TestGetBuildNotifications(unittest.TestCase): 8 | def setUp(self): 9 | self.exports = kojihub.RootExports() 10 | self.get_user = mock.patch('kojihub.kojihub.get_user').start() 11 | self.get_build_notifications = mock.patch('kojihub.kojihub.get_build_notifications').start() 12 | 13 | def tearDown(self): 14 | mock.patch.stopall() 15 | 16 | def test_loggedin_user(self): 17 | self.get_user.return_value = {'id': 1} 18 | kojihub.RootExports().getBuildNotifications(None) 19 | self.get_user.assert_called_once_with(None, strict=True) 20 | self.get_build_notifications.assert_called_once_with(1) 21 | 22 | def test_user_not_found(self): 23 | self.get_user.side_effect = koji.GenericError('error msg') 24 | with self.assertRaises(koji.GenericError) as cm: 25 | kojihub.RootExports().getBuildNotifications(1) 26 | self.get_user.assert_called_once_with(1, strict=True) 27 | self.get_build_notifications.assert_not_called() 28 | self.assertEqual(cm.exception.args[0], 'error msg') 29 | -------------------------------------------------------------------------------- /tests/test_cli/test_load_plugins.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | 3 | import os 4 | 5 | import unittest 6 | 7 | try: 8 | from unittest import mock 9 | except ImportError: 10 | import mock 11 | 12 | from . import loadcli 13 | 14 | cli = loadcli.cli 15 | 16 | 17 | class TestLoadPlugins(unittest.TestCase): 18 | @mock.patch('logging.getLogger') 19 | @mock.patch('os.path.isdir') 20 | def test_load_plugins(self, isdir, getLogger): 21 | # skip system path and default user plugin directory check 22 | isdir.side_effect = lambda path: False if path.startswith('/usr') \ 23 | or path == os.path.expanduser("~/.koji/plugins") \ 24 | else True 25 | cli.load_plugins(os.path.dirname(__file__) + '/data/cli_plugins1:' + 26 | os.path.dirname(__file__) + '/data/cli_plugins2') 27 | self.assertTrue(callable(cli.foobar)) 28 | self.assertTrue(callable(cli.foo2)) 29 | self.assertTrue(hasattr(cli, 'foo6')) 30 | self.assertFalse(hasattr(cli, 'foo3')) 31 | self.assertFalse(hasattr(cli, 'foo4')) 32 | self.assertTrue(hasattr(cli, 'foo5')) 33 | self.assertFalse(hasattr(cli, 'sth')) 34 | -------------------------------------------------------------------------------- /schemas/schema-upgrade-1.35-1.36.sql: -------------------------------------------------------------------------------- 1 | -- upgrade script to migrate the Koji database schema 2 | -- from version 1.35 to 1.36 3 | 4 | BEGIN; 5 | 6 | INSERT INTO archivetypes (name, description, extensions) VALUES ('erofs', 'erofs image', 'erofs') ON CONFLICT DO NOTHING; 7 | INSERT INTO archivetypes (name, description, extensions) VALUES ('erofs-compressed', 'Compressed erofs image', 'erofs.gz erofs.xz') ON CONFLICT DO NOTHING; 8 | INSERT INTO archivetypes (name, description, extensions) VALUES ('squashfs', 'SquashFS image', 'squashfs') ON CONFLICT DO NOTHING; 9 | INSERT INTO archivetypes (name, description, extensions) VALUES ('squashfs-compressed', 'Compressed SquashFS image', 'squashfs.gz squashfs.xz') ON CONFLICT DO NOTHING; 10 | 11 | -- https://pagure.io/koji/issue/3963 12 | CREATE INDEX IF NOT EXISTS standard_buildroot_host_id on standard_buildroot(host_id); 13 | CREATE INDEX IF NOT EXISTS standard_buildroot_repo_id on standard_buildroot(repo_id); 14 | CREATE INDEX IF NOT EXISTS standard_buildroot_task_id on standard_buildroot(task_id); 15 | CREATE INDEX IF NOT EXISTS standard_buildroot_create_event on standard_buildroot(create_event); 16 | CREATE INDEX IF NOT EXISTS standard_buildroot_retire_event on standard_buildroot(retire_event); 17 | 18 | COMMIT; 19 | -------------------------------------------------------------------------------- /docs/source/migrations/migrating_to_1.23.rst: -------------------------------------------------------------------------------- 1 | Migrating to Koji 1.23 2 | ====================== 3 | 4 | You should consider the following changes when migrating to 1.23: 5 | 6 | DB Updates 7 | ---------- 8 | 9 | This release includes some minor schema changes. 10 | 11 | We've dropped the ``NOT NULL`` restriction on the ``value`` column of the 12 | ``tag_extra`` table as part of the changes to allow blocking these values in 13 | the inheritance (see `PR#2495 `_). 14 | 15 | We've also added a new ``proton_queue`` table that is used by the ``protonmsg`` 16 | plugin when it is configured to queue messages 17 | (see `PR#2441 `_) 18 | 19 | Lastly, we've added a new index for the ``task`` table to improve performance 20 | (see `PR#2419 `_) 21 | 22 | As in previous releases, we provide a migration script that updates the database. 23 | 24 | :: 25 | 26 | # psql koji koji < /usr/share/doc/koji/docs/schema-upgrade-1.22-1.23.sql 27 | 28 | 29 | Other changes 30 | ------------- 31 | 32 | There are numerous other changes in 1.23 that should not have a direct impact on migration. For 33 | details see: :doc:`../release_notes/release_notes_1.23` 34 | -------------------------------------------------------------------------------- /tests/test_www/test_freetask.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from unittest import mock 4 | from .loadwebindex import webidx 5 | from koji.server import ServerRedirect 6 | 7 | 8 | class TestFreeTask(unittest.TestCase): 9 | def setUp(self): 10 | self.get_server = mock.patch.object(webidx, "_getServer").start() 11 | self.assert_login = mock.patch.object(webidx, "_assertLogin").start() 12 | self.server = mock.MagicMock() 13 | self.environ = { 14 | 'koji.options': { 15 | 'SiteName': 'test', 16 | 'KojiFilesURL': 'https://server.local/files', 17 | }, 18 | 'koji.currentUser': None 19 | } 20 | self.task_id = '1' 21 | 22 | def tearDown(self): 23 | mock.patch.stopall() 24 | 25 | def test_freetask_valid(self): 26 | """Test freetask function valid case.""" 27 | self.server.freeTask.return_value = None 28 | self.get_server.return_value = self.server 29 | 30 | with self.assertRaises(ServerRedirect): 31 | webidx.freetask(self.environ, self.task_id) 32 | self.server.freeTask.assert_called_with(int(self.task_id)) 33 | self.assertEqual(self.environ['koji.redirect'], f'taskinfo?taskID={self.task_id}') 34 | -------------------------------------------------------------------------------- /tests/test_hub/test_get_build_notification_blocks.py: -------------------------------------------------------------------------------- 1 | from unittest import mock 2 | import unittest 3 | import koji 4 | import kojihub 5 | 6 | 7 | class TestGetBuildNotificationBlocks(unittest.TestCase): 8 | def setUp(self): 9 | self.exports = kojihub.RootExports() 10 | self.get_user = mock.patch('kojihub.kojihub.get_user').start() 11 | self.get_build_notification_blocks = mock.patch( 12 | 'kojihub.kojihub.get_build_notification_blocks').start() 13 | 14 | def tearDown(self): 15 | mock.patch.stopall() 16 | 17 | def test_loggedin_user(self): 18 | self.get_user.return_value = {'id': 1} 19 | self.exports.getBuildNotificationBlocks(None) 20 | self.get_user.assert_called_once_with(None, strict=True) 21 | self.get_build_notification_blocks.assert_called_once_with(1) 22 | 23 | def test_user_not_found(self): 24 | self.get_user.side_effect = koji.GenericError('error msg') 25 | with self.assertRaises(koji.GenericError) as cm: 26 | self.exports.getBuildNotificationBlocks(1) 27 | self.get_user.assert_called_once_with(1, strict=True) 28 | self.get_build_notification_blocks.assert_not_called() 29 | self.assertEqual(cm.exception.args[0], 'error msg') 30 | -------------------------------------------------------------------------------- /tests/test_hub/test_get_volume.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from unittest import mock 3 | 4 | import koji 5 | import kojihub 6 | 7 | 8 | class TestGetVolume(unittest.TestCase): 9 | 10 | def setUp(self): 11 | self.exports = kojihub.RootExports() 12 | self.lookup_name = mock.patch('kojihub.kojihub.lookup_name').start() 13 | 14 | def tearDown(self): 15 | mock.patch.stopall() 16 | 17 | def test_non_exist_volume_with_strict(self): 18 | volume = ['test-volume'] 19 | self.lookup_name.return_value = None 20 | with self.assertRaises(koji.GenericError) as cm: 21 | self.exports.getVolume(volume, strict=True) 22 | self.assertEqual("No such volume: %s" % volume, str(cm.exception)) 23 | 24 | def test_non_exist_volume_without_strict(self): 25 | volume = ['test-volume'] 26 | self.lookup_name.return_value = None 27 | result = self.exports.getVolume(volume) 28 | self.assertEqual(None, result) 29 | 30 | def test_valid_volume(self): 31 | volume = ['test-volume'] 32 | volume_dict = {'id': 0, 'name': 'DEFAULT'} 33 | self.lookup_name.return_value = volume_dict 34 | result = self.exports.getVolume(volume) 35 | self.assertEqual(volume_dict, result) 36 | -------------------------------------------------------------------------------- /tests/test_www/test_resubmittask.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from unittest import mock 4 | from .loadwebindex import webidx 5 | from koji.server import ServerRedirect 6 | 7 | 8 | class TestResubmitTask(unittest.TestCase): 9 | def setUp(self): 10 | self.get_server = mock.patch.object(webidx, "_getServer").start() 11 | self.assert_login = mock.patch.object(webidx, "_assertLogin").start() 12 | self.server = mock.MagicMock() 13 | self.environ = { 14 | 'koji.options': { 15 | 'SiteName': 'test', 16 | 'KojiFilesURL': 'https://server.local/files', 17 | }, 18 | 'koji.currentUser': None 19 | } 20 | self.task_id = 1 21 | 22 | def tearDown(self): 23 | mock.patch.stopall() 24 | 25 | def test_resubmittask_valid(self): 26 | """Test resubmittask function valid case.""" 27 | self.server.resubmitTask.return_value = 2 28 | self.get_server.return_value = self.server 29 | 30 | with self.assertRaises(ServerRedirect): 31 | webidx.resubmittask(self.environ, str(self.task_id)) 32 | self.server.resubmitTask.assert_called_with(self.task_id) 33 | self.assertEqual(self.environ['koji.redirect'], 'taskinfo?taskID=2') 34 | -------------------------------------------------------------------------------- /tests/test_www/test_canceltask.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from unittest import mock 4 | from .loadwebindex import webidx 5 | from koji.server import ServerRedirect 6 | 7 | 8 | class TestCancelTask(unittest.TestCase): 9 | def setUp(self): 10 | self.get_server = mock.patch.object(webidx, "_getServer").start() 11 | self.assert_login = mock.patch.object(webidx, "_assertLogin").start() 12 | self.server = mock.MagicMock() 13 | self.environ = { 14 | 'koji.options': { 15 | 'SiteName': 'test', 16 | 'KojiFilesURL': 'https://server.local/files', 17 | }, 18 | 'koji.currentUser': None 19 | } 20 | self.task_id = '1' 21 | 22 | def tearDown(self): 23 | mock.patch.stopall() 24 | 25 | def test_canceltask_valid(self): 26 | """Test canceltask function valid case.""" 27 | self.server.cancelTask.return_value = None 28 | self.get_server.return_value = self.server 29 | 30 | with self.assertRaises(ServerRedirect): 31 | webidx.canceltask(self.environ, self.task_id) 32 | self.server.cancelTask.assert_called_with(int(self.task_id)) 33 | self.assertEqual(self.environ['koji.redirect'], f'taskinfo?taskID={self.task_id}') 34 | -------------------------------------------------------------------------------- /www/kojiweb/templates/api.html.j2: -------------------------------------------------------------------------------- 1 | #include "header.html.j2" 2 | 3 |

API reference (hub version: {{ koji_version }}, web version: {{ web_version }})

4 | 5 | Various constants used in API calls can be found in first part of koji module. 7 | Exceptions which can be raised in python client are just after constants section. 8 | More details about XMLRPC interface are documented 9 | here. 10 | 11 | Basic anonymous client in python would look like this: 12 | 13 |
14 | import koji
15 | 
16 | mytag = "mytag"
17 | session = koji.ClientSession("{{ koji_hub_url }}")
18 | try:
19 |     repo_info = session.getRepo(mytag, koji.REPO_STATES["READY"], dist=True)
20 |     if not repo_info:
21 |         print(f"There is no active dist repo for {mytag}")
22 | except koji.GenericError:
23 |     print(f"Tag {mytag} doesn't exist")
24 | 
25 | 26 |

List of API calls

27 |
    28 | #for method in methods 29 |
  • 30 |
    {{ method['name'] ~ method['argdesc'] }}
    31 |   {{ method['doc'] if method['doc'] is not none else '' }}
    32 |      
    33 |
  • 34 | #endfor 35 |
36 | 37 | #include "footer.html.j2" 38 | -------------------------------------------------------------------------------- /docs/source/migrations/migrating_to_1.18.rst: -------------------------------------------------------------------------------- 1 | Migrating to Koji 1.18 2 | ====================== 3 | 4 | .. 5 | reStructured Text formatted 6 | 7 | You should consider the following changes when migrating to 1.18: 8 | 9 | DB Updates 10 | ---------- 11 | 12 | This release has a few schema changes: 13 | 14 | * Several new indexes to speed operations 15 | * A ``cg_id`` field has been added to the ``build`` table 16 | * A new ``build_reservations`` table 17 | * A new ``build_notifications_block`` table 18 | * Updates to the data in the ``archivetypes`` table 19 | 20 | As in previous releases, we provide a migration script that updates the 21 | database. 22 | 23 | :: 24 | 25 | # psql koji koji 21 | 22 | 23 | class ServerError(Exception): 24 | """Base class for our server-side-only exceptions""" 25 | 26 | 27 | class ServerRedirect(ServerError): 28 | """Used to handle redirects""" 29 | 30 | 31 | class BadRequest(ServerError): 32 | """Used to trigger an http 400 error""" 33 | 34 | 35 | class RequestTimeout(ServerError): 36 | """Used to trigger an http 408 error""" 37 | -------------------------------------------------------------------------------- /docs/source/CVEs/CVE-2024-9427.rst: -------------------------------------------------------------------------------- 1 | ============= 2 | CVE-2024-9427 3 | ============= 4 | 5 | New XSS attack on kojiweb 6 | 7 | Summary 8 | ------- 9 | 10 | An unsanitized input allows for an XSS attack. Javascript code from a malicious 11 | link could be reflected in the resulting web page. At present, we do not 12 | believe that this can be used to submit an action or make a change in Koji due 13 | to existing XSS protections in the code. Even so, this is a serious issue and 14 | we recommend applying this update promptly. 15 | 16 | Bug fix 17 | ------- 18 | 19 | We are releasing updates for affected versions of Koji from within the 20 | past year. 21 | The following releases all contain the fix: 22 | 23 | - 1.35.1 24 | - 1.34.3 25 | - 1.33.2 26 | 27 | Anyone using a Koji version older than a year should update to a more 28 | current version as soon as possible. 29 | 30 | For users who have customized their Koji code, we recommend rebasing your work 31 | onto the appropriate update release. Please see Koji 32 | `issue #4204 `_ for the code details. 33 | 34 | As with all changes to web code, you must restart httpd for the changes to 35 | take effect. 36 | 37 | Links 38 | ----- 39 | 40 | Fixed versions can be found at our releases page: 41 | 42 | https://pagure.io/koji/releases 43 | -------------------------------------------------------------------------------- /docs/source/CVEs/CVE-2020-15856.rst: -------------------------------------------------------------------------------- 1 | ============== 2 | CVE-2020-15856 3 | ============== 4 | 5 | XSS attack on kojiweb 6 | 7 | Summary 8 | ------- 9 | 10 | Web interface can be abused by XSS attack. Attackers can supply subversive HTTP 11 | links containing malicious javascript code. Such links were not controlled 12 | properly, so attackers can potentially force users to submit actions which were 13 | not intended. Some actions which can be done via web UI can be destructive, so 14 | updating to this version is highly recommended. 15 | 16 | Bug fix 17 | ------- 18 | 19 | We are releasing updates for affected versions of Koji from within the 20 | past year. 21 | The following releases all contain the fix: 22 | 23 | - 1.23.1 24 | - 1.22.2 25 | - 1.21.2 26 | 27 | Anyone using a Koji version older than a year should update to a more 28 | current version as soon as possible. 29 | 30 | For users who have customized their Koji code, we recommend rebasing your work 31 | onto the appropriate update release. Please see Koji 32 | `issue #2645 `_ for the code details. 33 | 34 | As with all changes to web code, you must restart httpd for the changes to 35 | take effect. 36 | 37 | Links 38 | ----- 39 | 40 | Fixed versions can be found at our releases page: 41 | 42 | https://pagure.io/koji/releases 43 | -------------------------------------------------------------------------------- /tests/test_plugins/load_plugin.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | import os 3 | import sys 4 | 5 | def load_plugin(plugin_type, plugin_name): 6 | # We have to do this craziness because 'import koji' is ambiguous. Is it the 7 | # koji module, or the koji cli module. Jump through hoops accordingly. 8 | # https://stackoverflow.com/questions/67631/how-to-import-a-module-given-the-full-path 9 | mod_name = "%s_%s" % (plugin_name, plugin_type) 10 | CLI_FILENAME = os.path.join( 11 | os.path.dirname(__file__), 12 | "../../plugins", 13 | plugin_type, 14 | "%s.py" % plugin_name) 15 | sys.path = [os.path.dirname(CLI_FILENAME), 16 | os.path.join(os.path.dirname(__file__), "../..", plugin_type)] + \ 17 | sys.path 18 | if sys.version_info[0] >= 3: 19 | import importlib.machinery 20 | loader = importlib.machinery.SourceFileLoader(mod_name, CLI_FILENAME) 21 | spec = importlib.util.spec_from_loader(loader.name, loader) 22 | plugin = importlib.util.module_from_spec(spec) 23 | spec.loader.exec_module(plugin) 24 | loader.exec_module(plugin) 25 | sys.modules[mod_name] = plugin 26 | else: 27 | import imp 28 | plugin = imp.load_source(mod_name, CLI_FILENAME) 29 | return plugin 30 | -------------------------------------------------------------------------------- /tests/test_lib/test_rawheader_fields.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | from __future__ import absolute_import 3 | import os.path 4 | import unittest 5 | 6 | import koji 7 | 8 | 9 | class TestRawHeaderFields(unittest.TestCase): 10 | 11 | RPMFILES = [ 12 | "test-deps-1-1.fc24.x86_64.rpm", 13 | "test-files-1-1.fc27.noarch.rpm", 14 | "test-nosrc-1-1.fc24.nosrc.rpm", 15 | "test-deps-1-1.fc24.x86_64.rpm.signed", 16 | "test-nopatch-1-1.fc24.nosrc.rpm", 17 | "test-src-1-1.fc24.src.rpm", 18 | ] 19 | 20 | def test_header_sizes(self): 21 | for basename in self.RPMFILES: 22 | fn = os.path.join(os.path.dirname(__file__), 'data/rpms', basename) 23 | 24 | rh = koji.RawHeader(koji.rip_rpm_hdr(fn)) 25 | hdr = koji.get_rpm_header(fn) 26 | 27 | for key in rh.index: 28 | if key in (63, 1141): 29 | continue 30 | ours = rh.get(key, decode=True) 31 | theirs = hdr[key] 32 | if type(ours) != type(theirs): 33 | if isinstance(ours, list) and len(ours) == 1 and ours[0] == theirs: 34 | # rpm is presenting as a scalar 35 | continue 36 | # otherwise 37 | self.assertEqual(ours, theirs) 38 | -------------------------------------------------------------------------------- /tests/test_www/test_externalrepoinfo.py: -------------------------------------------------------------------------------- 1 | from unittest import mock 2 | import unittest 3 | 4 | from .loadwebindex import webidx 5 | 6 | 7 | class TestExternalRepoInfo(unittest.TestCase): 8 | def setUp(self): 9 | self.get_server = mock.patch.object(webidx, "_getServer").start() 10 | self.gen_html = mock.patch.object(webidx, '_genHTML').start() 11 | self.server = mock.MagicMock() 12 | self.environ = { 13 | 'koji.options': { 14 | 'SiteName': 'test', 15 | 'KojiFilesURL': 'https://server.local/files', 16 | }, 17 | 'koji.currentUser': None 18 | } 19 | self.extrepo_id = '111' 20 | 21 | def tearDown(self): 22 | mock.patch.stopall() 23 | 24 | def test_externalrepoinfo_valid(self): 25 | """Test externalrepoinfo function.""" 26 | self.get_server.return_value = self.server 27 | self.server.getExternalRepo.return_value = {'name': 'ext-repo', 'id': 111} 28 | self.server.getTagExternalRepos.return_value = {'id': 1, 'name': 'test-tag'} 29 | webidx.externalrepoinfo(self.environ, self.extrepo_id) 30 | self.server.getExternalRepo.assert_called_once_with(int(self.extrepo_id), strict=True) 31 | self.server.getTagExternalRepos.assert_called_once_with(repo_info=int(self.extrepo_id)) 32 | -------------------------------------------------------------------------------- /docs/source/release_notes/release_notes_1.15.1.rst: -------------------------------------------------------------------------------- 1 | Koji 1.15.1 Release Notes 2 | ========================= 3 | 4 | Koji 1.15.1 is a bugfix release for Koji 1.15. The most important change 5 | is the fix for :doc:`../CVEs/CVE-2018-1002150`. 6 | 7 | Please see: :doc:`release_notes_1.15` 8 | 9 | Issues fixed in 1.15.1 10 | ---------------------- 11 | 12 | - `Issue 850 `_ -- 13 | CVE-2018-1002150 14 | 15 | - `Issue 846 `_ -- 16 | error occurs in SCM.get_source since subprocess.check_output is not supported by python 2.6- 17 | 18 | - `Issue 724 `_ -- 19 | buildNotification of wrapperRPM fails because of task["label"] is None 20 | 21 | - `Issue 786 `_ -- 22 | buildSRPMFromSCM tasks fail on koji 1.15 23 | 24 | - `Issue 803 `_ -- 25 | Email notifications makes build tasks fail with "KeyError: 'users_usertype'" 26 | 27 | - `Issue 742 `_ -- 28 | dict key access fail in koji_cli.commands._build_image 29 | 30 | - `Issue 811 `_ -- 31 | AttributeError: 'dict' object has no attribute 'hub.checked_md5' 32 | 33 | - `Issue 813 `_ -- 34 | cg imports fail with "Unsupported checksum type" 35 | -------------------------------------------------------------------------------- /kojihub/__init__.py: -------------------------------------------------------------------------------- 1 | from .kojihub import * # noqa: F401 F403 2 | # import also all private functions for backward compatibility 3 | # new private methods should be imported via kojihub.kojihub 4 | from .kojihub import ( # noqa: F401 5 | _create_build_target, 6 | _create_tag, 7 | _delete_build, 8 | _delete_build_target, 9 | _delete_event_id, 10 | _delete_tag, 11 | _direct_pkglist_add, 12 | _direct_pkglist_remove, 13 | _direct_tag_build, 14 | _direct_untag_build, 15 | _edit_build_target, 16 | _edit_tag, 17 | _edit_user, 18 | _fix_archive_row, 19 | _fix_extra_field, 20 | _fix_rpm_row, 21 | _generate_maven_metadata, 22 | _get_archive_type_by_id, 23 | _get_archive_type_by_name, 24 | _get_build_target, 25 | _get_tarball_list, 26 | _get_zipfile_list, 27 | _grp_pkg_add, 28 | _grp_pkg_remove, 29 | _grp_pkg_unblock, 30 | _grp_req_add, 31 | _grp_req_remove, 32 | _grp_req_unblock, 33 | _grplist_add, 34 | _grplist_remove, 35 | _grplist_unblock, 36 | _import_archive_file, 37 | _import_wrapper, 38 | _pkglist_add, 39 | _pkglist_owner_add, 40 | _pkglist_owner_remove, 41 | _pkglist_remove, 42 | _scan_sighdr, 43 | _set_build_volume, 44 | _tag_build, 45 | _untag_build, 46 | _writeInheritanceData, 47 | _write_maven_repo_metadata, 48 | ) 49 | -------------------------------------------------------------------------------- /tests/test_hub/test_db/test_update_processor.py: -------------------------------------------------------------------------------- 1 | try: 2 | from unittest import mock 3 | except ImportError: 4 | import mock 5 | import unittest 6 | 7 | import kojihub 8 | 9 | 10 | class TestUpdateProcessor(unittest.TestCase): 11 | 12 | def test_basic_instantiation(self): 13 | kojihub.UpdateProcessor('sometable') # No exception! 14 | 15 | def test_to_string_with_data(self): 16 | proc = kojihub.UpdateProcessor('sometable', data={'foo': 'bar'}) 17 | actual = str(proc) 18 | expected = 'UPDATE sometable SET foo = %(data.foo)s' 19 | self.assertEqual(actual, expected) 20 | 21 | def test_to_values_from_data(self): 22 | proc = kojihub.UpdateProcessor('sometable', data={'foo': 'bar'}) 23 | actual = proc.get_values() 24 | expected = {'data.foo': 'bar'} 25 | self.assertEqual(actual, expected) 26 | 27 | @mock.patch('kojihub.db.context') 28 | def test_simple_execution_with_iterate(self, context_db): 29 | cursor = mock.MagicMock() 30 | context_db.cnx.cursor.return_value = cursor 31 | proc = kojihub.UpdateProcessor('sometable', data={'foo': 'bar'}) 32 | proc.execute() 33 | cursor.execute.assert_called_once_with( 34 | 'UPDATE sometable SET foo = %(data.foo)s', 35 | {'data.foo': 'bar'}, 36 | log_errors=True, 37 | ) 38 | -------------------------------------------------------------------------------- /util/koji-gc.conf: -------------------------------------------------------------------------------- 1 | #test policy file 2 | #earlier = higher precedence! 3 | 4 | [main] 5 | key_aliases = 6 | 30C9ECF8 fedora-test 7 | 4F2A6FD2 fedora-gold 8 | 897DA07A redhat-beta 9 | 1AC70CE6 fedora-extras 10 | 11 | unprotected_keys = 12 | fedora-test 13 | fedora-extras 14 | redhat-beta 15 | 16 | server = https://koji.fedoraproject.org/kojihub 17 | weburl = https://koji.fedoraproject.org/koji 18 | 19 | # The domain name that will be appended to Koji usernames 20 | # when creating email notifications 21 | #email_domain = fedoraproject.org 22 | 23 | # SMTP user and pass (uncomment and fill in if your smtp server requires authentication) 24 | #smtp_user=user@example.com 25 | #smtp_pass=CHANGEME 26 | 27 | [prune] 28 | policy = 29 | #stuff to protect 30 | #note that tags with master lock engaged are already protected 31 | tag *-updates :: keep 32 | age < 1 day :: skip 33 | sig fedora-gold :: skip 34 | sig fedora-test && age < 12 weeks :: keep 35 | 36 | #stuff to chuck semi-rapidly 37 | tag *-testing *-candidate :: { # nested rules 38 | order >= 2 :: untag 39 | order > 0 && age > 6 weeks :: untag 40 | } #closing braces must be on a line by themselves (modulo comments/whitespace) 41 | tag *-candidate && age > 60 weeks :: untag 42 | 43 | #default: keep the last 3 44 | order > 2 :: untag 45 | -------------------------------------------------------------------------------- /kojihub/Makefile: -------------------------------------------------------------------------------- 1 | PYVER_MAJOR := $(shell $(PYTHON) -c 'import sys; print(".".join(sys.version.split(".")[:1]))') 2 | PACKAGE = kojihub 3 | PYFILES = $(wildcard *.py) 4 | PKGDIR = $(shell $(PYTHON) ../devtools/get_site_packages.py )/$(PACKAGE) 5 | 6 | SERVERDIR = /usr/share/koji-hub 7 | 8 | _default: 9 | @echo "nothing to make. try make install" 10 | 11 | clean: 12 | rm -f *.o *.so *.pyc *~ 13 | rm -rf __pycache__ 14 | 15 | install: 16 | @if [ "$(DESTDIR)" = "" ]; then \ 17 | echo " "; \ 18 | echo "ERROR: A destdir is required"; \ 19 | exit 1; \ 20 | fi 21 | 22 | # python module 23 | mkdir -p $(DESTDIR)/$(PKGDIR) 24 | for p in $(PYFILES) ; do \ 25 | install -p -m 644 $$p $(DESTDIR)/$(PKGDIR)/$$p; \ 26 | done 27 | 28 | $(PYTHON) -c "import compileall; compileall.compile_dir('$(DESTDIR)/$(PKGDIR)', 1, '$(PYDIR)', 1)" 29 | 30 | # application files 31 | mkdir -p $(DESTDIR)/etc/httpd/conf.d 32 | install -p -m 644 app/httpd.conf $(DESTDIR)/etc/httpd/conf.d/kojihub.conf 33 | 34 | mkdir -p $(DESTDIR)/etc/koji-hub 35 | install -p -m 644 app/hub.conf $(DESTDIR)/etc/koji-hub/hub.conf 36 | mkdir -p $(DESTDIR)/etc/koji-hub/hub.conf.d 37 | 38 | 39 | mkdir -p $(DESTDIR)/$(SERVERDIR) 40 | install -p -m 644 app/kojiapp.py $(DESTDIR)/$(SERVERDIR)/kojiapp.py 41 | $(PYTHON) -c "import compileall; compileall.compile_dir('$(DESTDIR)/$(SERVERDIR)', 1, '$(PYDIR)', 1)" 42 | -------------------------------------------------------------------------------- /tests/test_www/test_userinfo.py: -------------------------------------------------------------------------------- 1 | from unittest import mock 2 | import unittest 3 | 4 | from .loadwebindex import webidx 5 | 6 | 7 | class TestUserInfo(unittest.TestCase): 8 | def setUp(self): 9 | self.get_server = mock.patch.object(webidx, "_getServer").start() 10 | self.gen_html = mock.patch.object(webidx, '_genHTML').start() 11 | self.server = mock.MagicMock() 12 | self.environ = { 13 | 'koji.options': { 14 | 'SiteName': 'test', 15 | 'KojiFilesURL': 'https://server.local/files', 16 | }, 17 | 'koji.currentUser': None 18 | } 19 | self.user_id = '5' 20 | 21 | def tearDown(self): 22 | mock.patch.stopall() 23 | 24 | def test_userinfo(self): 25 | """Test userinfo function""" 26 | self.get_server.return_value = self.server 27 | 28 | self.server.getUser.return_value = {'id': int(self.user_id), 'name': 'test-user'} 29 | self.server.listTasks.return_value = 123 30 | self.server.countAndFilterResults.return_value = [123, 'result'] 31 | 32 | webidx.userinfo(self.environ, self.user_id) 33 | self.server.getUser.assert_called_once_with(int(self.user_id), strict=True) 34 | self.server.listTasks.assert_called_once_with( 35 | opts={'owner': int(self.user_id), 'parent': None}, queryOpts={'countOnly': True}) 36 | -------------------------------------------------------------------------------- /koji/context.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2005-2014 Red Hat, Inc. 2 | # 3 | # Koji is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU Lesser General Public 5 | # License as published by the Free Software Foundation; 6 | # version 2.1 of the License. 7 | # 8 | # This software is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | # Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public 14 | # License along with this software; if not, write to the Free Software 15 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 16 | # 17 | # Authors: 18 | # Mike McLean 19 | 20 | # This module provides a threadlocal instance that kojihub uses to store 21 | # request context. 22 | # In the past, we had a custom ThreadLocal implementation, but now this is 23 | # just a thin wrapper around threading.local 24 | 25 | from __future__ import absolute_import 26 | 27 | import threading 28 | 29 | 30 | class ThreadLocal(threading.local): 31 | """A small compatibility wrapper around threading.local""" 32 | 33 | def _threadclear(self): 34 | self.__dict__.clear() 35 | 36 | 37 | context = ThreadLocal() 38 | -------------------------------------------------------------------------------- /tests/test_hub/test_build_image_indirection.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import koji 3 | import kojihub 4 | from unittest import mock 5 | 6 | 7 | class TestBuildImageIndirection(unittest.TestCase): 8 | 9 | def setUp(self): 10 | self.context = mock.patch('kojihub.kojihub.context').start() 11 | self.exports = kojihub.RootExports() 12 | self.context.session.assertPerm = mock.MagicMock() 13 | self.context.session.hasPerm = mock.MagicMock() 14 | 15 | def tearDown(self): 16 | mock.patch.stopall() 17 | 18 | def test_priority_without_admin(self): 19 | priority = -10 20 | self.context.session.assertPerm.side_effect = None 21 | self.context.session.hasPerm.return_value = False 22 | with self.assertRaises(koji.ActionNotAllowed) as cm: 23 | self.exports.buildImageIndirection(priority=priority) 24 | self.assertEqual("only admins may create high-priority tasks", str(cm.exception)) 25 | 26 | def test_opts_without_expected_keys(self): 27 | priority = 10 28 | opts = {} 29 | self.context.session.assertPerm.side_effect = None 30 | with self.assertRaises(koji.ActionNotAllowed) as cm: 31 | self.exports.buildImageIndirection(opts=opts, priority=priority) 32 | self.assertEqual("Non-scratch builds must provide url for the indirection template", 33 | str(cm.exception)) 34 | -------------------------------------------------------------------------------- /tests/test_lib/data/mock/simple.out: -------------------------------------------------------------------------------- 1 | # Auto-generated by the Koji build system 2 | 3 | config_opts['basedir'] = '/var/lib/mock' 4 | config_opts['chroot_setup_cmd'] = 'install @build' 5 | config_opts['chroothome'] = '/builddir' 6 | config_opts['dnf_warning'] = False 7 | config_opts['root'] = 'ROOTNAME' 8 | config_opts['rpmbuild_networking'] = False 9 | config_opts['rpmbuild_timeout'] = 86400 10 | config_opts['target_arch'] = 'x86_64' 11 | config_opts['use_bootstrap_image'] = False 12 | config_opts['use_host_resolv'] = False 13 | config_opts['yum.conf'] = '[main]\ncachedir=/var/cache/yum\ndebuglevel=1\nlogfile=/var/log/yum.log\nreposdir=/dev/null\nretries=20\nobsoletes=1\ngpgcheck=0\nassumeyes=1\nkeepcache=1\ninstall_weak_deps=0\nstrict=1\n\n# repos\n\n[build]\nname=build\nbaseurl=file:///mnt/koji/repos/TAG/99/x86_64\n' 14 | 15 | config_opts['plugin_conf']['ccache_enable'] = False 16 | config_opts['plugin_conf']['root_cache_enable'] = False 17 | config_opts['plugin_conf']['yum_cache_enable'] = False 18 | 19 | config_opts['macros']['%_host'] = 'x86_64-koji-linux-gnu' 20 | config_opts['macros']['%_host_cpu'] = 'x86_64' 21 | config_opts['macros']['%_rpmfilename'] = '%%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm' 22 | config_opts['macros']['%_topdir'] = '/builddir/build' 23 | config_opts['macros']['%distribution'] = 'Unknown' 24 | config_opts['macros']['%packager'] = 'Koji' 25 | config_opts['macros']['%vendor'] = 'Koji' 26 | 27 | -------------------------------------------------------------------------------- /tests/test_hub/test_list_cgs.py: -------------------------------------------------------------------------------- 1 | from unittest import mock 2 | 3 | import kojihub 4 | import kojihub.kojihub 5 | from .utils import DBQueryTestCase 6 | 7 | 8 | class TestListCGs(DBQueryTestCase): 9 | def setUp(self): 10 | super(TestListCGs, self).setUp() 11 | 12 | def tearDown(self): 13 | mock.patch.stopall() 14 | 15 | def test_valid(self): 16 | self.qp_iterate_return_value = [{'id': 1, 'name': 'cg_name_1', 'user_name': 'test-user'}, 17 | {'id': 2, 'name': 'cg_name_2', 'user_name': 'test-user'}] 18 | expected_result = {'cg_name_1': {'id': 1, 'users': ['test-user']}, 19 | 'cg_name_2': {'id': 2, 'users': ['test-user']}} 20 | rv = kojihub.list_cgs() 21 | self.assertEqual(rv, expected_result) 22 | 23 | self.assertEqual(len(self.queries), 1) 24 | query = self.queries[0] 25 | self.assertEqual(query.tables, ['cg_users']) 26 | self.assertEqual(query.columns, ['content_generator.id', 'content_generator.name', 27 | 'users.name']) 28 | self.assertEqual(query.clauses, ['cg_users.active = TRUE']) 29 | self.assertEqual(query.joins, ['content_generator ON content_generator.id = ' 30 | 'cg_users.cg_id', 'users ON users.id = cg_users.user_id']) 31 | self.assertEqual(query.values, {}) 32 | -------------------------------------------------------------------------------- /tests/test_hub/test_new_typed_build.py: -------------------------------------------------------------------------------- 1 | from unittest import mock 2 | import unittest 3 | 4 | import kojihub 5 | 6 | 7 | class TestNewTypedBuild(unittest.TestCase): 8 | 9 | @mock.patch('kojihub.kojihub.lookup_name') 10 | @mock.patch('kojihub.kojihub.QueryProcessor') 11 | @mock.patch('kojihub.kojihub.InsertProcessor') 12 | def test_new_typed_build(self, InsertProcessor, QueryProcessor, lookup_name): 13 | 14 | binfo = {'id': 1, 'foo': '137'} 15 | btype = 'sometype' 16 | lookup_name.return_value = {'id': 99, 'name': btype} 17 | 18 | # no current entry 19 | query = QueryProcessor.return_value 20 | query.executeOne.return_value = None 21 | insert = InsertProcessor.return_value 22 | kojihub.new_typed_build(binfo, btype) 23 | QueryProcessor.assert_called_once() 24 | query.executeOne.assert_called_once() 25 | InsertProcessor.assert_called_once() 26 | insert.execute.assert_called_once() 27 | 28 | InsertProcessor.reset_mock() 29 | QueryProcessor.reset_mock() 30 | 31 | # current entry 32 | query = QueryProcessor.return_value 33 | query.executeOne.return_value = {'build_id':binfo['id']} 34 | kojihub.new_typed_build(binfo, btype) 35 | QueryProcessor.assert_called_once() 36 | query.executeOne.assert_called_once() 37 | InsertProcessor.assert_not_called() 38 | -------------------------------------------------------------------------------- /tests/test_www/test_enablehost.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from unittest import mock 4 | from .loadwebindex import webidx 5 | from koji.server import ServerRedirect 6 | 7 | 8 | class TestEnableHost(unittest.TestCase): 9 | def setUp(self): 10 | self.get_server = mock.patch.object(webidx, "_getServer").start() 11 | self.assert_login = mock.patch.object(webidx, "_assertLogin").start() 12 | self.server = mock.MagicMock() 13 | self.environ = { 14 | 'koji.options': { 15 | 'SiteName': 'test', 16 | 'KojiFilesURL': 'https://server.local/files', 17 | }, 18 | 'koji.currentUser': None 19 | } 20 | self.host_id = '1' 21 | self.host_info = {'id': int(self.host_id), 'name': 'test-host'} 22 | 23 | def tearDown(self): 24 | mock.patch.stopall() 25 | 26 | def test_enablehost_valid(self): 27 | """Test enablehost function valid case.""" 28 | self.server.getHost.return_value = self.host_info 29 | self.server.enableHost.return_value = None 30 | self.get_server.return_value = self.server 31 | 32 | with self.assertRaises(ServerRedirect): 33 | webidx.enablehost(self.environ, self.host_id) 34 | self.server.enableHost.assert_called_with(self.host_info['name']) 35 | self.assertEqual(self.environ['koji.redirect'], f'hostinfo?hostID={self.host_id}') 36 | -------------------------------------------------------------------------------- /tests/test_www/test_disablehost.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from unittest import mock 4 | from .loadwebindex import webidx 5 | from koji.server import ServerRedirect 6 | 7 | 8 | class TestDisableHost(unittest.TestCase): 9 | def setUp(self): 10 | self.get_server = mock.patch.object(webidx, "_getServer").start() 11 | self.assert_login = mock.patch.object(webidx, "_assertLogin").start() 12 | self.server = mock.MagicMock() 13 | self.environ = { 14 | 'koji.options': { 15 | 'SiteName': 'test', 16 | 'KojiFilesURL': 'https://server.local/files', 17 | }, 18 | 'koji.currentUser': None 19 | } 20 | self.host_id = '1' 21 | self.host_info = {'id': int(self.host_id), 'name': 'test-host'} 22 | 23 | def tearDown(self): 24 | mock.patch.stopall() 25 | 26 | def test_disablehost_valid(self): 27 | """Test disablehost function valid case.""" 28 | self.server.getHost.return_value = self.host_info 29 | self.server.disableHost.return_value = None 30 | self.get_server.return_value = self.server 31 | 32 | with self.assertRaises(ServerRedirect): 33 | webidx.disablehost(self.environ, str(self.host_id)) 34 | self.server.disableHost.assert_called_with(self.host_info['name']) 35 | self.assertEqual(self.environ['koji.redirect'], f'hostinfo?hostID={self.host_id}') 36 | -------------------------------------------------------------------------------- /runtests: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2 2 | 3 | import subprocess 4 | import tempfile 5 | from multiprocessing import Pool 6 | 7 | 8 | def explode_ref(ref, dest): 9 | """Write contents from ref to dest dir""" 10 | p1 = subprocess.Popen(['git', 'archive', ref], stdout=subprocess.PIPE) 11 | p2 = subprocess.Popen(['tar', '-x'], cwd=dest, stdin=p1.stdout) 12 | p1.stdout.close() 13 | p2.communicate() 14 | 15 | 16 | def checkout_run(cmd, ref='HEAD'): 17 | """Run command in a fresh checkout of ref and return output""" 18 | tempdir = tempfile.mkdtemp(prefix='koji-runtests-') 19 | explode_ref(ref, tempdir) 20 | try: 21 | output = subprocess.check_output(cmd, cwd=tempdir, 22 | stderr=subprocess.STDOUT) 23 | except subprocess.CalledProcessError as e: 24 | print("Command failed with code %s" % e.returncode) 25 | output = e.output 26 | return output 27 | 28 | 29 | def run_test2(): 30 | return checkout_run(['make', 'test2']) 31 | 32 | 33 | def run_test3(): 34 | return checkout_run(['make', 'test3']) 35 | 36 | 37 | def main(): 38 | """Run our test targets in parallel using fresh checkouts of HEAD""" 39 | print("Running tests...") 40 | p = Pool(2) 41 | results = [p.apply_async(f) for f in [run_test2, run_test3]] 42 | for r in results: 43 | print(r.get().decode('utf-8')) 44 | p.close() 45 | 46 | 47 | if __name__ == '__main__': 48 | main() 49 | -------------------------------------------------------------------------------- /cli/koji.conf: -------------------------------------------------------------------------------- 1 | [koji] 2 | 3 | ;configuration for koji cli tool 4 | 5 | ;url of XMLRPC server 6 | ;server = http://hub.example.com/kojihub 7 | 8 | ;url of web interface 9 | ;weburl = http://www.example.com/koji 10 | 11 | ;url of package download site 12 | ;pkgurl = http://www.example.com/packages 13 | 14 | ;path to the koji top directory 15 | ;topdir = /mnt/koji 16 | 17 | ;configuration for Kerberos authentication 18 | 19 | ;the principal to auth as for automated clients 20 | ;principal = client@EXAMPLE.COM 21 | 22 | ;the keytab to auth as for automated clients 23 | ;keytab = /etc/krb5.keytab 24 | 25 | 26 | ;configuration for SSL authentication 27 | 28 | ;client certificate 29 | ;cert = ~/.koji/client.crt 30 | 31 | ;certificate of the CA that issued the HTTP server certificate 32 | ;serverca = ~/.koji/serverca.crt 33 | 34 | ;plugin paths, separated by ':' as the same as the shell's PATH 35 | ;koji_cli_plugins module and ~/.koji/plugins are always loaded in advance, 36 | ;and then be overridden by this option 37 | ;plugin_paths = ~/.koji/plugins 38 | 39 | ;[not_implemented_yet] 40 | ;enabled plugins for CLI, runroot and save_failed_tree are available 41 | ;plugins = 42 | 43 | ;timeout of XMLRPC requests by seconds, default: 60 * 60 * 12 = 43200 44 | ;timeout = 43200 45 | 46 | ;timeout of GSSAPI/SSL authentication by seconds, default: 60 47 | ;auth_timeout = 60 48 | 49 | ;enforcing CLI authentication even for anonymous calls 50 | ;force_auth = False 51 | -------------------------------------------------------------------------------- /tests/test_hub/test_convert_value.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | import koji 4 | from kojihub import convert_value 5 | 6 | 7 | class TestConvertValue(unittest.TestCase): 8 | def test_convert_ok(self): 9 | cases = ( 10 | (str, 'text'), 11 | (int, 1), 12 | (bool, True) 13 | ) 14 | for cast, input in cases: 15 | output = convert_value(input, cast=cast) 16 | self.assertEqual(input, output) 17 | 18 | def test_convert_fail(self): 19 | with self.assertRaises(koji.ParameterError): 20 | convert_value('asd', cast=int) 21 | 22 | def test_convert_exc(self): 23 | with self.assertRaises(IOError): 24 | convert_value('asd', cast=int, exc_type=IOError) 25 | 26 | def test_none(self): 27 | with self.assertRaises(koji.ParameterError): 28 | convert_value(None, cast=int) 29 | value = convert_value(None, cast=int, none_allowed=True) 30 | self.assertIsNone(value) 31 | 32 | def test_message(self): 33 | msg = 'test_message' 34 | with self.assertRaises(koji.ParameterError) as ex: 35 | convert_value(None, cast=int, message=msg) 36 | self.assertEqual(str(ex.exception), msg) 37 | 38 | def test_only(self): 39 | "fail on otherwise valid conversions" 40 | with self.assertRaises(koji.ParameterError): 41 | convert_value(None, cast=bool, check_only=True) 42 | 43 | -------------------------------------------------------------------------------- /tests/test_lib/data/mock/internaldev.out: -------------------------------------------------------------------------------- 1 | # Auto-generated by the Koji build system 2 | 3 | config_opts['basedir'] = '/var/lib/mock' 4 | config_opts['chroot_setup_cmd'] = 'install @build' 5 | config_opts['chroothome'] = '/builddir' 6 | config_opts['dnf_warning'] = False 7 | config_opts['internal_dev_setup'] = False 8 | config_opts['root'] = 'ROOTNAME' 9 | config_opts['rpmbuild_networking'] = False 10 | config_opts['rpmbuild_timeout'] = 86400 11 | config_opts['target_arch'] = 'x86_64' 12 | config_opts['use_bootstrap_image'] = False 13 | config_opts['use_host_resolv'] = False 14 | config_opts['yum.conf'] = '[main]\ncachedir=/var/cache/yum\ndebuglevel=1\nlogfile=/var/log/yum.log\nreposdir=/dev/null\nretries=20\nobsoletes=1\ngpgcheck=0\nassumeyes=1\nkeepcache=1\ninstall_weak_deps=0\nstrict=1\n\n# repos\n\n[build]\nname=build\nbaseurl=file:///mnt/koji/repos/TAG/99/x86_64\n' 15 | 16 | config_opts['plugin_conf']['ccache_enable'] = False 17 | config_opts['plugin_conf']['root_cache_enable'] = False 18 | config_opts['plugin_conf']['yum_cache_enable'] = False 19 | 20 | config_opts['macros']['%_host'] = 'x86_64-koji-linux-gnu' 21 | config_opts['macros']['%_host_cpu'] = 'x86_64' 22 | config_opts['macros']['%_rpmfilename'] = '%%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm' 23 | config_opts['macros']['%_topdir'] = '/builddir/build' 24 | config_opts['macros']['%distribution'] = 'Unknown' 25 | config_opts['macros']['%packager'] = 'Koji' 26 | config_opts['macros']['%vendor'] = 'Koji' 27 | 28 | -------------------------------------------------------------------------------- /tests/test_lib/data/mock/internaldev2.out: -------------------------------------------------------------------------------- 1 | # Auto-generated by the Koji build system 2 | 3 | config_opts['basedir'] = '/var/lib/mock' 4 | config_opts['chroot_setup_cmd'] = 'install @build' 5 | config_opts['chroothome'] = '/builddir' 6 | config_opts['dnf_warning'] = False 7 | config_opts['internal_dev_setup'] = True 8 | config_opts['root'] = 'ROOTNAME' 9 | config_opts['rpmbuild_networking'] = False 10 | config_opts['rpmbuild_timeout'] = 86400 11 | config_opts['target_arch'] = 'x86_64' 12 | config_opts['use_bootstrap_image'] = False 13 | config_opts['use_host_resolv'] = False 14 | config_opts['yum.conf'] = '[main]\ncachedir=/var/cache/yum\ndebuglevel=1\nlogfile=/var/log/yum.log\nreposdir=/dev/null\nretries=20\nobsoletes=1\ngpgcheck=0\nassumeyes=1\nkeepcache=1\ninstall_weak_deps=0\nstrict=1\n\n# repos\n\n[build]\nname=build\nbaseurl=file:///mnt/koji/repos/TAG/99/x86_64\n' 15 | 16 | config_opts['plugin_conf']['ccache_enable'] = False 17 | config_opts['plugin_conf']['root_cache_enable'] = False 18 | config_opts['plugin_conf']['yum_cache_enable'] = False 19 | 20 | config_opts['macros']['%_host'] = 'x86_64-koji-linux-gnu' 21 | config_opts['macros']['%_host_cpu'] = 'x86_64' 22 | config_opts['macros']['%_rpmfilename'] = '%%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm' 23 | config_opts['macros']['%_topdir'] = '/builddir/build' 24 | config_opts['macros']['%distribution'] = 'Unknown' 25 | config_opts['macros']['%packager'] = 'Koji' 26 | config_opts['macros']['%vendor'] = 'Koji' 27 | 28 | -------------------------------------------------------------------------------- /util/Makefile: -------------------------------------------------------------------------------- 1 | BINFILES = kojira koji-gc koji-shadow koji-sweep-db koji-sidetag-cleanup 2 | SBINDIR ?= /usr/sbin 3 | SYSTEMDSYSTEMUNITDIR = $(shell pkg-config systemd --variable=systemdsystemunitdir) 4 | 5 | _default: 6 | @echo "nothing to make. try make install" 7 | 8 | clean: 9 | rm -f *.o *.so *.pyc *~ 10 | rm -rf __pycache__ 11 | for b in $(BINFILES); do rm -f $${b}c; done 12 | 13 | _install: 14 | @if [ "$(DESTDIR)" = "" ]; then \ 15 | echo " "; \ 16 | echo "ERROR: A destdir is required"; \ 17 | exit 1; \ 18 | fi 19 | mkdir -p $(DESTDIR)$(SBINDIR) 20 | install -p -m 755 $(BINFILES) $(DESTDIR)$(SBINDIR) 21 | 22 | mkdir -p $(DESTDIR)/etc/kojira 23 | install -p -m 644 kojira.conf $(DESTDIR)/etc/kojira/kojira.conf 24 | 25 | mkdir -p $(DESTDIR)/etc/koji-gc 26 | install -p -m 644 koji-gc.conf $(DESTDIR)/etc/koji-gc/koji-gc.conf 27 | install -p -m 644 email.tpl $(DESTDIR)/etc/koji-gc/email.tpl 28 | 29 | mkdir -p $(DESTDIR)/etc/koji-shadow 30 | install -p -m 644 koji-shadow.conf $(DESTDIR)/etc/koji-shadow/koji-shadow.conf 31 | 32 | install: _install 33 | mkdir -p $(DESTDIR)$(SYSTEMDSYSTEMUNITDIR) 34 | install -p -m 644 kojira.service $(DESTDIR)$(SYSTEMDSYSTEMUNITDIR) 35 | install -p -m 644 koji-gc.service $(DESTDIR)$(SYSTEMDSYSTEMUNITDIR) 36 | install -p -m 644 koji-gc.timer $(DESTDIR)$(SYSTEMDSYSTEMUNITDIR) 37 | install -p -m 644 koji-sweep-db.service $(DESTDIR)$(SYSTEMDSYSTEMUNITDIR) 38 | install -p -m 644 koji-sweep-db.timer $(DESTDIR)$(SYSTEMDSYSTEMUNITDIR) 39 | -------------------------------------------------------------------------------- /tests/test_hub/test_list_packages_simple.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import kojihub 3 | from unittest import mock 4 | 5 | QP = kojihub.QueryProcessor 6 | 7 | 8 | class TestListPackagesSimple(unittest.TestCase): 9 | 10 | def setUp(self): 11 | self.maxDiff = None 12 | self.QueryProcessor = mock.patch('kojihub.kojihub.QueryProcessor', 13 | side_effect=self.get_query).start() 14 | self.queries = [] 15 | self.exports = kojihub.RootExports() 16 | 17 | def tearDown(self): 18 | mock.patch.stopall() 19 | 20 | def get_query(self, *args, **kwargs): 21 | query = QP(*args, **kwargs) 22 | query.execute = mock.MagicMock() 23 | self.queries.append(query) 24 | return query 25 | 26 | def test_prefix_not_none(self): 27 | self.exports.listPackagesSimple('test-prefix') 28 | self.assertEqual(len(self.queries), 1) 29 | query = self.queries[0] 30 | self.assertEqual(query.tables, ['package']) 31 | self.assertEqual(query.clauses, ["package.name ILIKE %(prefix)s || '%%'"]) 32 | self.assertEqual(query.joins, None) 33 | 34 | def test_prefix_is_none(self): 35 | self.exports.listPackagesSimple() 36 | self.assertEqual(len(self.queries), 1) 37 | query = self.queries[0] 38 | self.assertEqual(query.tables, ['package']) 39 | self.assertEqual(query.clauses, None) 40 | self.assertEqual(query.joins, None) 41 | -------------------------------------------------------------------------------- /docs/source/CVEs/CVE-2019-17109.rst: -------------------------------------------------------------------------------- 1 | ============== 2 | CVE-2019-17109 3 | ============== 4 | 5 | Koji hub allows arbitrary upload destinations 6 | 7 | 8 | Summary 9 | ------- 10 | 11 | The way that the hub code validates upload paths allows for an attacker to 12 | choose an arbitrary destination for the uploaded file. 13 | 14 | Uploading still requires login. However, an attacker with credentials could 15 | damage the integrity of the Koji system. 16 | 17 | There is no known workaround. All Koji admins are encouraged to update to a 18 | fixed version as soon as possible. 19 | 20 | 21 | 22 | Bug fix 23 | ------- 24 | 25 | We are releasing updates for affected versions of Koji from within the 26 | past two years. 27 | The following releases all contain the fix: 28 | 29 | - 1.18.1 30 | - 1.17.1 31 | - 1.16.3 32 | - 1.15.3 33 | - 1.14.3 34 | 35 | Note: the legacy-py24 branch is unaffected since it is client-only (no hub). 36 | 37 | Anyone using a Koji version older than two years should update to a more 38 | current version as soon as possible. 39 | 40 | For users who have customized their Koji code, we recommend rebasing your work 41 | onto the appropriate update release. Please see Koji 42 | `issue #1634 `_ for the code details. 43 | 44 | As with all changes to hub code, you must restart httpd for the changes to 45 | take effect. 46 | 47 | 48 | Links 49 | ----- 50 | 51 | Fixed versions can be found at our releases page: 52 | 53 | https://pagure.io/koji/releases 54 | -------------------------------------------------------------------------------- /plugins/hub/dud.py: -------------------------------------------------------------------------------- 1 | import koji 2 | import koji.tasks 3 | import kojihub 4 | 5 | from koji.context import context 6 | from koji.plugin import export 7 | 8 | koji.tasks.LEGACY_SIGNATURES['dudBuild'] = [ 9 | [['dud_name', 'dud_version', 'arches', 'target', 'pkg_list', 'opts'], 10 | None, None, (None,)]] 11 | koji.tasks.LEGACY_SIGNATURES['createDudIso'] = [ 12 | [['dud_name', 'dud_version', 'dud_release', 'arch', 13 | 'target_info', 'build_tag', 'repo_info', 'pkg_list', 'opts'], 14 | None, None, (None,)]] 15 | 16 | # /usr/lib/koji-hub-plugins/ 17 | 18 | 19 | @export 20 | def dudBuild(dud_name, dud_version, arches, target, pkg_list, optional_arches=None, scratch=False, 21 | alldeps=False, scmurl=None, priority=None): 22 | context.session.assertPerm('image') 23 | taskOpts = { 24 | 'channel': 'image', 25 | } 26 | if priority: 27 | if priority < 0: 28 | if not context.session.hasPerm('admin'): 29 | raise koji.ActionNotAllowed( 30 | 'only admins may create high-priority tasks') 31 | taskOpts['priority'] = koji.PRIO_DEFAULT + priority 32 | 33 | opts = { 34 | 'scratch': scratch, 35 | 'alldeps': alldeps, 36 | 'scmurl': scmurl, 37 | 'optional_arches': optional_arches, 38 | } 39 | 40 | return kojihub.make_task('dudBuild', 41 | [dud_name, dud_version, arches, target, pkg_list, opts], 42 | **taskOpts) 43 | -------------------------------------------------------------------------------- /tests/test_lib/test_profiles.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | import koji 3 | import sys 4 | import threading 5 | import traceback 6 | try: 7 | from unittest import mock 8 | except ImportError: 9 | import mock 10 | from six.moves import range 11 | 12 | import unittest 13 | 14 | 15 | class ProfilesTestCase(unittest.TestCase): 16 | 17 | def test_profile_threading(self): 18 | """ Test that profiles thread safe""" 19 | # see: https://pagure.io/koji/issue/58 and https://pagure.io/pungi/issue/253 20 | # loop a few times to increase chances of hitting race conditions 21 | for i in range(256): 22 | errors = {} 23 | threads = [threading.Thread(target=stress, args=(errors, _)) for _ in range(100)] 24 | for t in threads: 25 | t.start() 26 | for t in threads: 27 | t.join(30) 28 | for n in errors: 29 | err = errors[n] 30 | if err is not None: 31 | print(err) 32 | assert False 33 | 34 | 35 | def stress(errors, n): 36 | errors[n] = "Failed to start" 37 | try: 38 | config = mock.Mock(topdir='topdir') 39 | koji.get_profile_module('koji', config=config) 40 | except Exception: 41 | # if we don't catch this, pytest seems to ignore the test 42 | errors[n] = ''.join(traceback.format_exception(*sys.exc_info())) 43 | return 44 | else: 45 | errors[n] = None 46 | -------------------------------------------------------------------------------- /www/static/errors/unauthorized.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | Authentication Failed | Koji 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 | 17 | 18 | 21 | 22 |
23 |

Kerberos Authentication Failed

24 | The Koji Web UI was unable to verify your Kerberos credentials. Please make sure that you have valid 25 | Kerberos tickets (obtainable via kinit), and that you have 26 | configured your browser correctly. 27 |
28 | 29 | 32 | 33 |
34 |
35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /tests/test_www/test_activesessiondelete.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | import unittest 3 | from unittest import mock 4 | 5 | from koji.server import ServerRedirect 6 | from .loadwebindex import webidx 7 | 8 | 9 | class TestActiveSessionDelete(unittest.TestCase): 10 | def setUp(self): 11 | self.get_server = mock.patch.object(webidx, "_getServer").start() 12 | self.assert_login = mock.patch.object(webidx, "_assertLogin").start() 13 | self.server = mock.MagicMock() 14 | self.environ = { 15 | 'koji.options': { 16 | 'SiteName': 'test', 17 | 'KojiFilesURL': 'https://server.local/files', 18 | }, 19 | 'koji.currentUser': None, 20 | 21 | } 22 | 23 | def __get_server(env): 24 | env['koji.session'] = self.server 25 | return self.server 26 | 27 | self.get_server.side_effect = __get_server 28 | 29 | def tearDown(self): 30 | mock.patch.stopall() 31 | 32 | def test_activesessiondelete(self): 33 | """Test activesessiondelete function.""" 34 | session_id = 1 35 | self.get_server.return_value = self.server 36 | self.server.logout.return_value = None 37 | 38 | with self.assertRaises(ServerRedirect): 39 | webidx.activesessiondelete(self.environ, session_id) 40 | self.assertEqual(self.environ['koji.redirect'], 'activesession') 41 | self.server.logout.assert_called_with(session_id=session_id) 42 | -------------------------------------------------------------------------------- /tests/test_builder/data/calls/build_notif_1/message.txt: -------------------------------------------------------------------------------- 1 | From: koji@example.com 2 | Subject: Package: sisu-0.3.0-0.2.M1.fc23 Tag: f23 Status: complete Built by: user 3 | To: user@example.com 4 | X-Koji-Tag: f23 5 | X-Koji-Package: sisu 6 | X-Koji-Builder: user 7 | X-Koji-Status: complete 8 | X-Koji-Draft: False 9 | 10 | Package: sisu-0.3.0-0.2.M1.fc23 11 | Tag: f23 12 | Status: complete 13 | Built by: user 14 | ID: 612609 15 | Started: Wed, 18 Feb 2015 09:50:37 EST 16 | Finished: Wed, 18 Feb 2015 09:57:37 EST 17 | Changelog: 18 | * Wed Feb 18 2015 Happy Koji User - 1:0.3.0-0.2.M1 19 | - Unbundle ASM 20 | - Resolves: rhbz#1085903 21 | 22 | * Wed Feb 04 2015 Happy Koji User - 1:0.3.0-0.1.M1 23 | - Update to upstream milestone 0.3.0.M1 24 | 25 | * Tue Sep 30 2014 Happy Koji User - 1:0.2.1-10 26 | - Port to plexus-utils 3.0.18 27 | 28 | 29 | 30 | Closed tasks: 31 | ------------- 32 | 33 | Task 8982172 on buildvm-26.build.example.com 34 | Task Type: build (rawhide, /sisu:5428a6665ed5d4920bda5da6db6c9fce942070ac) 35 | 36 | Task 8982174 on arm02-builder13.build.example.com 37 | Task Type: buildSRPMFromSCM (/sisu:5428a6665ed5d4920bda5da6db6c9fce942070ac) 38 | 39 | Task 8982231 on buildvm-23.build.example.com 40 | Task Type: buildArch (sisu-0.3.0-0.2.M1.fc23.src.rpm, noarch) 41 | 42 | 43 | 44 | Task Info: https://koji.example.com/kojiweb/taskinfo?taskID=8982172 45 | Build Info: https://koji.example.com/kojiweb/buildinfo?buildID=612609 46 | -------------------------------------------------------------------------------- /tests/test_hub/test_create_rpm_checksum.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from unittest import mock 3 | 4 | import kojihub 5 | 6 | QP = kojihub.QueryProcessor 7 | 8 | 9 | class TestCreateRPMChecksum(unittest.TestCase): 10 | def setUp(self): 11 | self.maxDiff = None 12 | self.QueryProcessor = mock.patch('kojihub.kojihub.QueryProcessor', 13 | side_effect=self.getQuery).start() 14 | self.queries = [] 15 | self.query_execute = mock.MagicMock() 16 | 17 | def tearDown(self): 18 | mock.patch.stopall() 19 | 20 | def getQuery(self, *args, **kwargs): 21 | query = QP(*args, **kwargs) 22 | query.execute = self.query_execute 23 | self.queries.append(query) 24 | return query 25 | 26 | def test_checksum_exists(self): 27 | rpm_id = 123 28 | chsum_dict = {'md5': 'chsum-1', 'sha256': 'chsum-2'} 29 | sigkey = 'test-sigkey' 30 | self.query_execute.return_value = [{'checksum_type': 'md5'}, {'checksum_type': 'sha256'}] 31 | result = kojihub.create_rpm_checksum(rpm_id, sigkey, chsum_dict) 32 | self.assertIsNone(result) 33 | 34 | self.assertEqual(len(self.queries), 1) 35 | query = self.queries[0] 36 | self.assertEqual(query.tables, ['rpm_checksum']) 37 | self.assertEqual(query.joins, None) 38 | self.assertEqual(set(query.clauses), {"checksum_type IN %(checksum_types)s", 39 | "sigkey=%(sigkey)s", "rpm_id = %(rpm_id)d"}) 40 | -------------------------------------------------------------------------------- /docs/source/migrations/migrating_to_1.13.rst: -------------------------------------------------------------------------------- 1 | Migrating to Koji 1.13 2 | ====================== 3 | 4 | .. 5 | reStructured Text formatted 6 | 7 | The 1.13 release of Koji includes several changes that you should consider when 8 | migrating. 9 | 10 | DB Updates 11 | ---------- 12 | 13 | We have increased the length limit for tag names and there is a minor schema 14 | change to support this. 15 | 16 | As in previous releases, we provide a migration script that updates the 17 | database. 18 | 19 | :: 20 | 21 | # psql koji koji ", str(cm.exception)) 25 | 26 | def test_volume_non_exist_wrong_type(self): 27 | self.exports.getVolume.side_effect = koji.GenericError 28 | with self.assertRaises(koji.GenericError): 29 | self.exports.downloadTaskOutput(self.task_id, self.filename, volume=self.volumename) 30 | 31 | def test_filename_wrong_format(self): 32 | filename = '../test-file' 33 | volumeinfo = {'id': 1, 'name': self.volumename} 34 | self.exports.getVolume.return_value = volumeinfo 35 | with self.assertRaises(koji.GenericError) as cm: 36 | self.exports.downloadTaskOutput(self.task_id, filename, volume=self.volumename) 37 | self.assertEqual(f"Invalid file name: {filename}", str(cm.exception)) 38 | -------------------------------------------------------------------------------- /devtools/README.md: -------------------------------------------------------------------------------- 1 | Koji Developer Tools 2 | -------------------- 3 | 4 | This directory contains some tools that developers may find useful. 5 | 6 | fakehub 7 | ------- 8 | 9 | This script runs a single hub call in the foreground (no httpd) and 10 | dumps the results. It runs using the code from the checkout. 11 | 12 | The call to be executed is specified on the command line, much like 13 | the ``koji call`` command. 14 | 15 | For example: 16 | ``` 17 | [mike@localhost koji]$ devtools/fakehub getTag 1 18 | ``` 19 | 20 | You will see hub logs on the console. The call result is pretty printed 21 | at the end. 22 | 23 | This tool makes it possible to run hub code through the debugger or 24 | or profiler with relative ease. 25 | 26 | fakehub looks for ``fakehub.conf`` or ``fakehub.conf.d`` in the devtools 27 | directory. If either is present, then 28 | ``koji.hub.ConfigFile`` and ``koji.hub.ConfigDir`` are set to these values. 29 | If neither is, then the code will fall back to the default (system) config. 30 | 31 | 32 | fakeweb 33 | ------- 34 | 35 | This tool is similar to the fakehub tool, but instead of a single pass it starts 36 | a web server on port 8000 and starts serving. 37 | 38 | As with the fakehub tool, fakeweb runs in the foreground in a single thread, making 39 | it possible to debug web code. 40 | 41 | Similar to fakehub, fakeweb looks for ``fakeweb.conf`` or ``fakeweb.conf.d`` 42 | in the devtools directory. If either is present, then 43 | ``koji.web.ConfigFile`` and ``koji.web.ConfigDir`` are set to these values. 44 | If neither is, then the code will fall back to the default (system) config. 45 | -------------------------------------------------------------------------------- /tests/test_cli/test_running_in_bg.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | try: 3 | from unittest import mock 4 | except ImportError: 5 | import mock 6 | import unittest 7 | 8 | from koji_cli.lib import _running_in_bg 9 | 10 | class TestRunningInBg(unittest.TestCase): 11 | 12 | @mock.patch('koji_cli.lib.os') 13 | def test_running_in_bg(self, os_mock): 14 | os_mock.isatty.return_value = False 15 | self.assertTrue(_running_in_bg()) 16 | os_mock.isatty.return_value = True 17 | os_mock.getpgrp.return_value = 0 18 | os_mock.tcgetpgrp.return_value = 1 19 | self.assertTrue(_running_in_bg()) 20 | os_mock.tcgetpgrp.return_value = 0 21 | self.assertFalse(_running_in_bg()) 22 | 23 | os_mock.reset_mock() 24 | os_mock.tcgetpgrp.side_effect = OSError 25 | self.assertTrue(_running_in_bg()) 26 | os_mock.isatty.assert_called() 27 | os_mock.getpgrp.assert_called() 28 | os_mock.tcgetpgrp.assert_called() 29 | 30 | os_mock.reset_mock() 31 | os_mock.getpgrp.side_effect = OSError 32 | self.assertTrue(_running_in_bg()) 33 | os_mock.isatty.assert_called() 34 | os_mock.getpgrp.assert_called() 35 | os_mock.tcgetpgrp.assert_not_called() 36 | 37 | os_mock.reset_mock() 38 | os_mock.isatty.side_effect = OSError 39 | self.assertTrue(_running_in_bg()) 40 | os_mock.isatty.assert_called() 41 | os_mock.getpgrp.assert_not_called() 42 | os_mock.tcgetpgrp.assert_not_called() 43 | 44 | 45 | if __name__ == '__main__': 46 | unittest.main() 47 | -------------------------------------------------------------------------------- /tests/test_cli/test_list_volumes.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | try: 3 | from unittest import mock 4 | except ImportError: 5 | import mock 6 | import six 7 | import unittest 8 | 9 | from koji_cli.commands import anon_handle_list_volumes 10 | from . import utils 11 | 12 | 13 | class TestListVolumes(utils.CliTestCase): 14 | 15 | # Show long diffs in error output... 16 | maxDiff = None 17 | 18 | @mock.patch('sys.stdout', new_callable=six.StringIO) 19 | @mock.patch('koji_cli.commands.ensure_connection') 20 | def test_anon_handle_list_volumes( 21 | self, 22 | ensure_connection_mock, 23 | stdout): 24 | """Test anon_handle_list_volumes function""" 25 | session = mock.MagicMock() 26 | options = mock.MagicMock() 27 | vol_info = [ 28 | {'id': 1, 'name': 'DEFAULT'}, 29 | {'id': 2, 'name': 'TEST-1'}, 30 | {'id': 3, 'name': 'TEST-2'}, 31 | ] 32 | 33 | expected = "\n".join([v['name'] for v in vol_info]) + "\n" 34 | session.listVolumes.return_value = vol_info 35 | anon_handle_list_volumes(options, session, []) 36 | self.assert_console_message(stdout, expected) 37 | 38 | def test_anon_handle_list_volumes_help(self): 39 | self.assert_help( 40 | anon_handle_list_volumes, 41 | """Usage: %s list-volumes 42 | (Specify the --help global option for a list of other help options) 43 | 44 | Options: 45 | -h, --help show this help message and exit 46 | """ % self.progname) 47 | 48 | 49 | if __name__ == '__main__': 50 | unittest.main() 51 | -------------------------------------------------------------------------------- /tests/test_hub/test_db/test_upsert_processor.py: -------------------------------------------------------------------------------- 1 | from unittest import mock 2 | import unittest 3 | 4 | import koji 5 | import kojihub 6 | 7 | 8 | class TestUpsertProcessor(unittest.TestCase): 9 | def setUp(self): 10 | self.context_db = mock.patch('kojihub.db.context').start() 11 | 12 | def tearDown(self): 13 | mock.patch.stopall() 14 | 15 | def test_required_args(self): 16 | with self.assertRaises(ValueError) as e: 17 | proc = kojihub.UpsertProcessor('sometable') 18 | self.assertEqual(e.msg, 'either keys or skip_dup must be set') 19 | 20 | def test_skip_dup(self): 21 | proc = kojihub.UpsertProcessor('sometable', data={'foo': 'bar'}, skip_dup=True) 22 | actual = str(proc) 23 | expected = 'INSERT INTO sometable (foo) VALUES (%(foo)s) ON CONFLICT DO NOTHING' 24 | self.assertEqual(actual, expected) 25 | 26 | def test_key(self): 27 | proc = kojihub.UpsertProcessor('sometable', data={'id': 1, 'foo': 'bar'}, keys=['id']) 28 | actual = str(proc) 29 | expected = 'INSERT INTO sometable (foo, id) VALUES (%(foo)s, %(id)s) ON CONFLICT (id) DO UPDATE SET foo = %(foo)s' 30 | self.assertEqual(actual, expected) 31 | 32 | def test_keys(self): 33 | proc = kojihub.UpsertProcessor('sometable', data={'id': 1, 'package': 'koji', 'foo': 'bar'}, keys=['id', 'package']) 34 | actual = str(proc) 35 | expected = 'INSERT INTO sometable (foo, id, package) VALUES (%(foo)s, %(id)s, %(package)s) ' \ 36 | 'ON CONFLICT (id,package) DO UPDATE SET foo = %(foo)s' 37 | self.assertEqual(actual, expected) 38 | 39 | -------------------------------------------------------------------------------- /schemas/schema-upgrade-1.27-1.28.sql: -------------------------------------------------------------------------------- 1 | -- upgrade script to migrate the Koji database schema 2 | -- from version 1.27 to 1.28 3 | 4 | 5 | BEGIN; 6 | 7 | ALTER TABLE permissions ADD COLUMN description TEXT; 8 | 9 | UPDATE permissions set description='Full administrator access. Perform all actions.' WHERE name = 'admin'; 10 | UPDATE permissions set description='Create appliance builds - deprecated.' WHERE name = 'appliance'; 11 | UPDATE permissions set description='Create a dist-repo.' WHERE name = 'dist-repo'; 12 | UPDATE permissions set description='Add, remove, enable, disable hosts and channels.' WHERE name = 'host'; 13 | UPDATE permissions set description='Start image tasks.' WHERE name = 'image'; 14 | UPDATE permissions set description='Import image archives.' WHERE name = 'image-import'; 15 | UPDATE permissions set description='Start livecd tasks.' WHERE name = 'livecd'; 16 | UPDATE permissions set description='Import maven archives.' WHERE name = 'maven-import'; 17 | UPDATE permissions set description='Manage repos: newRepo, repoExpire, repoDelete, repoProblem.' WHERE name = 'repo'; 18 | UPDATE permissions set description='Import RPM signatures and write signed RPMs.' WHERE name = 'sign'; 19 | UPDATE permissions set description='Manage packages in tags: add, block, remove, and clone tags.' WHERE name = 'tag'; 20 | UPDATE permissions set description='Add, edit, and remove targets.' WHERE name = 'target'; 21 | UPDATE permissions set description='The default hub policy rule for "vm" requires this permission to trigger Windows builds.' WHERE name = 'win-admin'; 22 | UPDATE permissions set description='Import win archives.' WHERE name = 'win-import'; 23 | 24 | COMMIT; 25 | -------------------------------------------------------------------------------- /tests/test_www/test_packageinfo.py: -------------------------------------------------------------------------------- 1 | from unittest import mock 2 | import unittest 3 | 4 | import koji 5 | from .loadwebindex import webidx 6 | 7 | 8 | class TestPackageInfo(unittest.TestCase): 9 | def setUp(self): 10 | self.get_server = mock.patch.object(webidx, "_getServer").start() 11 | self.gen_html = mock.patch.object(webidx, '_genHTML').start() 12 | self.server = mock.MagicMock() 13 | 14 | self.environ = { 15 | 'koji.options': { 16 | 'SiteName': 'test', 17 | 'KojiFilesURL': 'https://server.local/files', 18 | }, 19 | 'koji.currentUser': None 20 | } 21 | self.package_id = '1' 22 | 23 | def tearDown(self): 24 | mock.patch.stopall() 25 | 26 | def test_packageinfo_exception(self): 27 | """Test taskinfo function raises exception""" 28 | self.server.getPackage.return_value = None 29 | 30 | self.get_server.return_value = self.server 31 | 32 | with self.assertRaises(koji.GenericError) as cm: 33 | webidx.packageinfo(self.environ, self.package_id) 34 | self.assertEqual(str(cm.exception), f'No such package ID: {self.package_id}') 35 | self.server.getPackage.assert_called_once_with(int(self.package_id)) 36 | 37 | def test_packageinfo_valid(self): 38 | """Test taskinfo function""" 39 | self.server.getPackage.return_value = {'id': self.package_id, 'name': 'test-pkg'} 40 | 41 | self.get_server.return_value = self.server 42 | webidx.packageinfo(self.environ, self.package_id) 43 | self.server.getPackage.assert_called_once_with(int(self.package_id)) 44 | -------------------------------------------------------------------------------- /tests/test_hub/test_get_external_repo_list.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from unittest import mock 4 | 5 | import kojihub 6 | 7 | 8 | class TestGetExternalRepo(unittest.TestCase): 9 | 10 | def setUp(self): 11 | self.get_tag = mock.patch('kojihub.kojihub.get_tag').start() 12 | self.read_full_inheritance = mock.patch('kojihub.kojihub.readFullInheritance').start() 13 | self.get_tag_external_repos = mock.patch('kojihub.kojihub.get_tag_external_repos').start() 14 | 15 | def tearDown(self): 16 | mock.patch.stopall() 17 | 18 | def test_valid(self): 19 | self.get_tag.return_value = {'id': 123} 20 | self.read_full_inheritance.return_value = [{'parent_id': 111}, {'parent_id': 112}] 21 | self.get_tag_external_repos.side_effect = [ 22 | [{'external_repo_id': 10, 'tag_id': 123}], 23 | [{'external_repo_id': 11, 'tag_id': 111}, {'external_repo_id': 12, 'tag_id': 111}], 24 | [{'external_repo_id': 13, 'tag_id': 112}]] 25 | result = kojihub.get_external_repo_list('test-tag') 26 | self.assertEqual(result, [ 27 | {'external_repo_id': 10, 'tag_id': 123}, 28 | {'external_repo_id': 11, 'tag_id': 111}, {'external_repo_id': 12, 'tag_id': 111}, 29 | {'external_repo_id': 13, 'tag_id': 112}]) 30 | self.get_tag.assert_called_once_with('test-tag', strict=True, event=None) 31 | self.read_full_inheritance.assert_called_once_with(123, None) 32 | self.get_tag_external_repos.assert_has_calls( 33 | [mock.call(tag_info=123, event=None), mock.call(tag_info=111, event=None), 34 | mock.call(tag_info=112, event=None)]) 35 | --------------------------------------------------------------------------------