diff --git a/awx/main/credential_plugins/github_app.py b/awx/main/credential_plugins/github_app.py new file mode 100644 index 0000000000..df302e0162 --- /dev/null +++ b/awx/main/credential_plugins/github_app.py @@ -0,0 +1,176 @@ +"""GitHub App Installation Access Token Credential Plugin. + +This module defines a credential plugin for making use of the +GitHub Apps mechanism, allowing authentication via GitHub App +installation-scoped access tokens. + +Functions: + +- :func:`extract_github_app_install_token`: Generates a GitHub App + Installation token. +- ``github_app_lookup``: Defines the credential plugin interface. +""" + +from github import Auth as Auth, Github +from github.Consts import DEFAULT_BASE_URL as PUBLIC_GH_API_URL +from github.GithubException import ( + BadAttributeException, + GithubException, + UnknownObjectException, +) + +from django.utils.translation import gettext_lazy as _ + +from .plugin import CredentialPlugin + +github_app_inputs = { + 'fields': [ + { + 'id': 'github_api_url', + 'label': _('GitHub API endpoint URL'), + 'type': 'string', + 'help_text': _( + 'Specify the GitHub API URL here. In the case of an Enterprise: ' + 'https://gh.your.org/api/v3 (self-hosted) ' + 'or https://api.SUBDOMAIN.ghe.com (cloud)', + ), + 'default': 'https://api.github.com', + }, + { + 'id': 'app_or_client_id', + 'label': _('GitHub App ID'), + 'type': 'string', + 'help_text': _( + 'The GitHub App ID created by the GitHub Admin. ' + 'Example App ID: 1121547 ' + 'found on https://github.com/settings/apps/ ' + 'required for creating a JWT token for authentication.', + ), + }, + { + 'id': 'install_id', + 'label': _('GitHub App Installation ID'), + 'type': 'string', + 'help_text': _( + 'The Installation ID from the GitHub App installation ' + 'generated by the GitHub Admin. ' + 'Example: 59980338 extracted from the installation link ' + 'https://github.com/settings/installations/59980338 ' + 'required for creating a limited GitHub app token.', + ), + }, + { + 'id': 'private_rsa_key', + 'label': _('RSA Private Key'), + 'type': 'string', + 'format': 'ssh_private_key', + 'secret': True, + 'multiline': True, + 'help_text': _( + 'Paste the contents of the PEM file that the GitHub Admin provided to you with the app and installation IDs.', + ), + }, + ], + 'metadata': [ + { + 'id': 'description', + 'label': _('Description (Optional)'), + 'type': 'string', + 'help_text': _('To be removed after UI is updated'), + }, + ], + 'required': ['app_or_client_id', 'install_id', 'private_rsa_key'], +} + +GH_CLIENT_ID_TRAILER_LENGTH = 16 +HEXADECIMAL_BASE = 16 + + +def _is_intish(app_id_candidate): + return isinstance(app_id_candidate, int) or app_id_candidate.isdigit() + + +def _is_client_id(client_id_candidate): + client_id_prefix = 'Iv1.' + if not client_id_candidate.startswith(client_id_prefix): + return False + + client_id_trailer = client_id_candidate[len(client_id_prefix) :] + + if len(client_id_trailer) != GH_CLIENT_ID_TRAILER_LENGTH: + return False + + try: + int(client_id_trailer, base=HEXADECIMAL_BASE) + except ValueError: + return False + + return True + + +def _is_app_or_client_id(app_or_client_id_candidate): + if _is_intish(app_or_client_id_candidate): + return True + return _is_client_id(app_or_client_id_candidate) + + +def _assert_ids_look_acceptable(app_or_client_id, install_id): + if not _is_app_or_client_id(app_or_client_id): + raise ValueError( + 'Expected GitHub App or Client ID to be an integer or a string ' + f'starting with `Iv1.` followed by 16 hexadecimal digits, ' + f'but got {app_or_client_id !r}', + ) + if isinstance(app_or_client_id, str) and _is_client_id(app_or_client_id): + raise ValueError( + 'Expected GitHub App ID must be an integer or a string ' + f'with an all-digit value, but got {app_or_client_id !r}. ' + 'Client IDs are currently unsupported.', + ) + if not _is_intish(install_id): + raise ValueError( + 'Expected GitHub App Installation ID to be an integer' f' but got {install_id !r}', + ) + + +def extract_github_app_install_token(github_api_url, app_or_client_id, private_rsa_key, install_id, **_discarded_kwargs): + """Generate a GH App Installation access token.""" + _assert_ids_look_acceptable(app_or_client_id, install_id) + + auth = Auth.AppAuth( + app_id=str(app_or_client_id), + private_key=private_rsa_key, + ).get_installation_auth(installation_id=int(install_id)) + + Github( + auth=auth, + base_url=github_api_url if github_api_url else PUBLIC_GH_API_URL, + ) + + doc_url = 'See https://docs.github.com/rest/reference/apps#create-an-installation-access-token-for-an-app' + app_install_context = f'app_or_client_id: {app_or_client_id}, install_id: {install_id}' + + try: + return auth.token + except UnknownObjectException as github_install_not_found_exc: + raise ValueError( + f'Failed to retrieve a GitHub installation token from {github_api_url} using {app_install_context}. Is the app installed? {doc_url}.' + f'\n\n{github_install_not_found_exc}', + ) from github_install_not_found_exc + except GithubException as pygithub_catchall_exc: + raise RuntimeError( + f'An unexpected error happened while talking to GitHub API @ {github_api_url} ({app_install_context}). ' + f'Is the app or client ID correct? And the private RSA key? {doc_url}.' + f'\n\n{pygithub_catchall_exc}', + ) from pygithub_catchall_exc + except BadAttributeException as github_broken_exc: + raise RuntimeError( + f'Broken GitHub @ {github_api_url} with {app_install_context}. It is a bug, please report it to the developers.\n\n{github_broken_exc}', + ) from github_broken_exc + + +github_app_lookup_plugin = CredentialPlugin( + 'GitHub App Installation Access Token Lookup', + inputs=github_app_inputs, + backend=extract_github_app_install_token, +) diff --git a/awx/main/tests/functional/github_app_test.py b/awx/main/tests/functional/github_app_test.py new file mode 100644 index 0000000000..2341a03654 --- /dev/null +++ b/awx/main/tests/functional/github_app_test.py @@ -0,0 +1,344 @@ +"""Tests for GitHub App Installation access token extraction plugin.""" + +from typing import TypedDict + +import pytest +from pytest_mock import MockerFixture + +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives.asymmetric.rsa import ( + RSAPrivateKey, + RSAPublicKey, + generate_private_key, +) +from cryptography.hazmat.primitives.serialization import ( + Encoding, + NoEncryption, + PrivateFormat, + PublicFormat, +) +from github.Auth import AppInstallationAuth +from github.Consts import DEFAULT_JWT_ALGORITHM +from github.GithubException import ( + BadAttributeException, + GithubException, + UnknownObjectException, +) +from jwt import decode as decode_jwt + +from awx.main.credential_plugins import github_app + + +github_app_jwt_client_id_unsupported = pytest.mark.xfail( + raises=(AssertionError, ValueError), + reason='Client ID in JWT is not currently supported by ' 'PyGitHub and is disabled.\n\n' 'Ref: https://github.com/PyGithub/PyGithub/issues/3213', +) + + +RSA_PUBLIC_EXPONENT = 65_537 # noqa: WPS303 +MINIMUM_RSA_KEY_SIZE = 1024 # the lowest value chosen for performance in tests + + +@pytest.fixture(scope='module') +def rsa_private_key() -> RSAPrivateKey: + """Generate an RSA private key.""" + return generate_private_key( + public_exponent=RSA_PUBLIC_EXPONENT, + key_size=MINIMUM_RSA_KEY_SIZE, # would be 4096 or higher in production + backend=default_backend(), + ) + + +@pytest.fixture(scope='module') +def rsa_public_key(rsa_private_key: RSAPrivateKey) -> RSAPublicKey: + """Extract a public key out of the private one.""" + return rsa_private_key.public_key() + + +@pytest.fixture(scope='module') +def rsa_private_key_bytes(rsa_private_key: RSAPrivateKey) -> bytes: + r"""Generate an unencrypted PKCS#1 formatted RSA private key. + + Encoded as PEM-bytes. + + This is what the GitHub-downloaded PEM files contain. + + Ref: https://developer.github.com/apps/building-github-apps/\ + authenticating-with-github-apps/ + """ + return rsa_private_key.private_bytes( + encoding=Encoding.PEM, + format=PrivateFormat.TraditionalOpenSSL, # A.K.A. PKCS#1 + encryption_algorithm=NoEncryption(), + ) + + +@pytest.fixture(scope='module') +def rsa_private_key_str(rsa_private_key_bytes: bytes) -> str: + """Return private key as an instance of string.""" + return rsa_private_key_bytes.decode('utf-8') + + +@pytest.fixture(scope='module') +def rsa_public_key_bytes(rsa_public_key: RSAPublicKey) -> bytes: + """Return a PKCS#1 formatted RSA public key encoded as PEM.""" + return rsa_public_key.public_bytes( + encoding=Encoding.PEM, + format=PublicFormat.PKCS1, + ) + + +class AppInstallIds(TypedDict): + """Schema for augmented extractor function keyword args.""" + + app_or_client_id: str + install_id: str + + +@pytest.mark.parametrize( + ('extract_github_app_install_token_args', 'expected_error_msg'), + ( + pytest.param( + { + 'app_or_client_id': 'invalid', + 'install_id': '666', + }, + '^Expected GitHub App or Client ID to be an integer or a string ' r'starting with `Iv1\.` followed by 16 hexadecimal digits, but got' " 'invalid'$", + id='gh-app-id-broken-text', + ), + pytest.param( + { + 'app_or_client_id': 'Iv1.bbbbbbbbbbbbbbb', + 'install_id': '666', + }, + '^Expected GitHub App or Client ID to be an integer or a string ' + r'starting with `Iv1\.` followed by 16 hexadecimal digits, but got' + " 'Iv1.bbbbbbbbbbbbbbb'$", + id='gh-app-id-client-id-not-enough-chars', + ), + pytest.param( + { + 'app_or_client_id': 'Iv1.bbbbbbbbbbbbbbbx', + 'install_id': '666', + }, + '^Expected GitHub App or Client ID to be an integer or a string ' + r'starting with `Iv1\.` followed by 16 hexadecimal digits, but got' + " 'Iv1.bbbbbbbbbbbbbbbx'$", + id='gh-app-id-client-id-broken-hex', + ), + pytest.param( + { + 'app_or_client_id': 'Iv1.bbbbbbbbbbbbbbbbb', + 'install_id': '666', + }, + '^Expected GitHub App or Client ID to be an integer or a string ' + r'starting with `Iv1\.` followed by 16 hexadecimal digits, but got' + " 'Iv1.bbbbbbbbbbbbbbbbb'$", + id='gh-app-id-client-id-too-many-chars', + ), + pytest.param( + { + 'app_or_client_id': 999, + 'install_id': 'invalid', + }, + '^Expected GitHub App Installation ID to be an integer ' "but got 'invalid'$", + id='gh-app-invalid-install-id-with-int-app-id', + ), + pytest.param( + { + 'app_or_client_id': '999', + 'install_id': 'invalid', + }, + '^Expected GitHub App Installation ID to be an integer ' "but got 'invalid'$", + id='gh-app-invalid-install-id-with-str-digit-app-id', + ), + pytest.param( + { + 'app_or_client_id': 'Iv1.cccccccccccccccc', + 'install_id': 'invalid', + }, + '^Expected GitHub App Installation ID to be an integer ' "but got 'invalid'$", + id='gh-app-invalid-install-id-with-client-id', + marks=github_app_jwt_client_id_unsupported, + ), + ), +) +def test_github_app_invalid_args( + extract_github_app_install_token_args: AppInstallIds, + expected_error_msg: str, +) -> None: + """Test that invalid arguments make token extractor bail early.""" + with pytest.raises(ValueError, match=expected_error_msg): + github_app.extract_github_app_install_token( + github_api_url='https://github.com', + private_rsa_key='key', + **extract_github_app_install_token_args, + ) + + +@pytest.mark.parametrize( + ( + 'github_exception', + 'transformed_exception', + 'error_msg', + ), + ( + ( + BadAttributeException( + '', + {}, + Exception(), + ), + RuntimeError, + ( + r'^Broken GitHub @ https://github\.com with ' + r'app_or_client_id: 123, install_id: 456\. It is a bug, ' + 'please report it to the ' + r"developers\.\n\n\('', \{\}, Exception\(\)\)$" + ), + ), + ( + GithubException(-1), + RuntimeError, + ( + '^An unexpected error happened while talking to GitHub API ' + r'@ https://github\.com ' + r'\(app_or_client_id: 123, install_id: 456\)\. ' + r'Is the app or client ID correct\? ' + r'And the private RSA key\? ' + r'See https://docs\.github\.com/rest/reference/apps' + r'#create-an-installation-access-token-for-an-app\.' + r'\n\n-1$' + ), + ), + ( + UnknownObjectException(-1), + ValueError, + ( + '^Failed to retrieve a GitHub installation token from ' + r'https://github\.com using ' + r'app_or_client_id: 123, install_id: 456\. ' + r'Is the app installed\? See ' + r'https://docs\.github\.com/rest/reference/apps' + r'#create-an-installation-access-token-for-an-app\.' + r'\n\n-1$' + ), + ), + ), + ids=( + 'github-broken', + 'unexpected-error', + 'no-install', + ), +) +def test_github_app_api_errors( + mocker: MockerFixture, + github_exception: Exception, + transformed_exception: type[Exception], + error_msg: str, +) -> None: + """Test successful GitHub authentication.""" + application_id = 123 + installation_id = 456 + + mocker.patch.object( + github_app.Auth.AppInstallationAuth, + 'token', + new_callable=mocker.PropertyMock, + side_effect=github_exception, + ) + + with pytest.raises(transformed_exception, match=error_msg): + github_app.extract_github_app_install_token( + github_api_url='https://github.com', + app_or_client_id=application_id, + install_id=installation_id, + private_rsa_key='key', + ) + + +class _FakeAppInstallationAuth(AppInstallationAuth): + @property + def token(self: '_FakeAppInstallationAuth') -> str: + return 'token-sentinel' + + +@pytest.mark.parametrize( + 'application_id', + ( + 123, + '123', + pytest.param( + 'Iv1.aaaaaaaaaaaaaaaa', + marks=github_app_jwt_client_id_unsupported, + ), + ), + ids=('app-id-int', 'app-id-str', 'client-id'), +) +@pytest.mark.parametrize( + 'installation_id', + (456, '456'), + ids=('install-id-int', 'install-id-str'), +) +# pylint: disable-next=too-many-arguments,too-many-positional-arguments +def test_github_app_github_authentication( # noqa: WPS211 + application_id: int | str, + installation_id: int | str, + mocker: MockerFixture, + monkeypatch: pytest.MonkeyPatch, + rsa_private_key_str: str, + rsa_public_key_bytes: bytes, +) -> None: + """Test successful GitHub authentication.""" + monkeypatch.setattr( + github_app.Auth, + 'AppInstallationAuth', + _FakeAppInstallationAuth, + ) + + get_installation_auth_spy = mocker.spy( + github_app.Auth, + 'AppInstallationAuth', + ) + github_initializer_spy = mocker.spy(github_app, 'Github') + + token = github_app.extract_github_app_install_token( + github_api_url='https://github.com', + app_or_client_id=application_id, + install_id=installation_id, + private_rsa_key=rsa_private_key_str, + ) + + observed_pygithub_obj = github_initializer_spy.spy_return + observed_gh_install_auth_obj = get_installation_auth_spy.spy_return + # pylint: disable-next=protected-access + signed_jwt = observed_gh_install_auth_obj._app_auth.token # noqa: WPS437 + + assert token == 'token-sentinel' + + assert observed_pygithub_obj.requester.base_url == 'https://github.com' + + assert observed_gh_install_auth_obj.installation_id == int(installation_id) + assert isinstance(observed_gh_install_auth_obj, _FakeAppInstallationAuth) + + # NOTE: The `decode_jwt()` call asserts that no + # NOTE: `jwt.exceptions.InvalidSignatureError()` exception gets raised + # NOTE: which would indicate incorrect RSA key or corrupted payload if + # NOTE: that was to happen. This verifies that JWT is signed with the + # NOTE: private RSA key we passed by using its public counterpart. + decode_jwt( + signed_jwt, + key=rsa_public_key_bytes, + algorithms=[DEFAULT_JWT_ALGORITHM], + options={ + 'require': ['exp', 'iat', 'iss'], + 'strict_aud': False, + 'verify_aud': True, + 'verify_exp': True, + 'verify_signature': True, + 'verify_nbf': True, + }, + audience=None, # GH App JWT don't set the audience claim + issuer=str(application_id), + leeway=0.001, # noqa: WPS432 + ) diff --git a/awx/main/tests/functional/test_credential.py b/awx/main/tests/functional/test_credential.py index 97cf2beb2d..4f45ff62f4 100644 --- a/awx/main/tests/functional/test_credential.py +++ b/awx/main/tests/functional/test_credential.py @@ -88,6 +88,7 @@ def test_default_cred_types(): 'galaxy_api_token', 'gce', 'github_token', + 'github_app', 'gitlab_token', 'gpg_public_key', 'hashivault_kv', diff --git a/licenses/PyGithub.txt b/licenses/PyGithub.txt new file mode 100644 index 0000000000..65c5ca88a6 --- /dev/null +++ b/licenses/PyGithub.txt @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/licenses/PyNaCl.txt b/licenses/PyNaCl.txt new file mode 100644 index 0000000000..91e18a62b6 --- /dev/null +++ b/licenses/PyNaCl.txt @@ -0,0 +1,174 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. diff --git a/licenses/pygithub-2.6.0.tar.gz b/licenses/pygithub-2.6.0.tar.gz new file mode 100644 index 0000000000..91a2591c13 Binary files /dev/null and b/licenses/pygithub-2.6.0.tar.gz differ diff --git a/licenses/pynacl.txt b/licenses/pynacl.txt new file mode 100644 index 0000000000..fa694622e3 --- /dev/null +++ b/licenses/pynacl.txt @@ -0,0 +1,174 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. \ No newline at end of file diff --git a/requirements/requirements.in b/requirements/requirements.in index 4bfd3368cd..eca90202ed 100644 --- a/requirements/requirements.in +++ b/requirements/requirements.in @@ -50,6 +50,7 @@ prometheus_client psycopg psutil pygerduty +PyGithub <= 2.6.0 pyopenssl>=23.2.0 # resolve dep conflict from cryptography pin above pyparsing==2.4.6 # Upgrading to v3 of pyparsing introduce errors on smart host filtering: Expected 'or' term, found 'or' (at char 15), (line:1, col:16) python-daemon>3.0.0 diff --git a/requirements/requirements.txt b/requirements/requirements.txt index 08e3ad92a8..aa7d110714 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -30,7 +30,6 @@ asgiref==3.7.2 # channels-redis # daphne # django - # django-ansible-base # django-cors-headers asn1==2.7.0 # via -r /awx_devel/requirements/requirements.in @@ -94,6 +93,7 @@ cffi==1.16.0 # via # cryptography # pycares + # pynacl channels==3.0.5 # via # -r /awx_devel/requirements/requirements.in @@ -135,6 +135,7 @@ deprecated==1.2.14 # opentelemetry-api # opentelemetry-exporter-otlp-proto-grpc # opentelemetry-exporter-otlp-proto-http + # pygithub distro==1.9.0 # via -r /awx_devel/requirements/requirements.in django==4.2.18 @@ -414,13 +415,17 @@ pydantic-core==2.14.1 # via pydantic pygerduty==0.38.3 # via -r /awx_devel/requirements/requirements.in +pygithub==2.6.0 + # via -r /awx_devel/requirements/requirements.in pyjwt[crypto]==2.8.0 # via # adal - # django-ansible-base # msal + # pygithub # social-auth-core # twilio +pynacl==1.5.0 + # via pygithub pyopenssl==24.0.0 # via # -r /awx_devel/requirements/requirements.in @@ -480,13 +485,13 @@ requests==2.32.3 # -r /awx_devel/requirements/requirements.in # adal # azure-core - # django-ansible-base # django-oauth-toolkit # kubernetes # msal # msrest # opa-python-client # opentelemetry-exporter-otlp-proto-http + # pygithub # python-dsv-sdk # python-tss-sdk # requests-oauthlib @@ -540,7 +545,6 @@ sqlparse==0.5.0 # via # -r /awx_devel/requirements/requirements.in # django - # django-ansible-base tacacs-plus==1.0 # via -r /awx_devel/requirements/requirements.in tempora==5.5.1 @@ -574,14 +578,15 @@ typing-extensions==4.9.0 # psycopg # pydantic # pydantic-core + # pygithub # setuptools-scm # twisted urllib3==1.26.20 # via # -r /awx_devel/requirements/requirements.in # botocore - # django-ansible-base # kubernetes + # pygithub # requests uwsgi==2.0.28 # via -r /awx_devel/requirements/requirements.in diff --git a/setup.cfg b/setup.cfg index c861d3ae92..1430906129 100644 --- a/setup.cfg +++ b/setup.cfg @@ -23,3 +23,4 @@ awx.credential_plugins = thycotic_dsv = awx.main.credential_plugins.dsv:dsv_plugin thycotic_tss = awx.main.credential_plugins.tss:tss_plugin aws_secretsmanager_credential = awx.main.credential_plugins.aws_secretsmanager:aws_secretmanager_plugin + github_app = awx.main.credential_plugins.github_app:github_app_lookup_plugin