diff --git a/.dockerignore b/.dockerignore index e328e5b..a0d39b1 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,3 +1,4 @@ LICENSE README.md .gitignore + diff --git a/Pipfile b/Pipfile index 0e0a396..89807e2 100644 --- a/Pipfile +++ b/Pipfile @@ -4,9 +4,12 @@ verify_ssl = true name = "pypi" [packages] -jwt = "==1.3.1" requests = "==2.32.2" +jwt = "==1.3.1" +cryptography = "==42.0.5" +[dev-packages] +python-dotenv = "==1.0.1" [requires] -python_version = "3" \ No newline at end of file +python_version = "3" diff --git a/Pipfile.lock b/Pipfile.lock index a0eba84..d647c37 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "08a8e4542a20e4b58bb45b8fdaa0cd90fe112938967c00c384fc83d8ce15ec75" + "sha256": "73def19ab14311a626a7adea02b0aaf7c26ce8037f8982202853d83bbcf786ad" }, "pipfile-spec": 6, "requires": { @@ -213,6 +213,7 @@ "sha256:f12764b8fffc7a123f641d7d049d382b73f96a34117e0b637b80643169cec8ac", "sha256:f8837fe1d6ac4a8052a9a8ddab256bc006242696f03368a4009be7ee3075cdb7" ], + "index": "pypi", "markers": "python_version >= '3.7'", "version": "==42.0.5" }, @@ -234,10 +235,11 @@ }, "pycparser": { "hashes": [ - "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9", - "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206" + "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6", + "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc" ], - "version": "==2.21" + "markers": "python_version >= '3.8'", + "version": "==2.22" }, "requests": { "hashes": [ @@ -257,5 +259,15 @@ "version": "==2.2.2" } }, - "develop": {} + "develop": { + "python-dotenv": { + "hashes": [ + "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca", + "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==1.0.1" + } + } } diff --git a/README.md b/README.md index cdd7a1a..9eafad2 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,7 @@ # github-access-using-githubapp github-access-using-githubapp + + +# Reference + +[generating-an-installation-access-token](https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/generating-an-installation-access-token-for-a-github-app#generating-an-installation-access-token) \ No newline at end of file diff --git a/generate_jwt.py b/generate_jwt.py index 61c1d06..dec902e 100644 --- a/generate_jwt.py +++ b/generate_jwt.py @@ -2,7 +2,8 @@ import time import argparse import os - +import requests +from dotenv import load_dotenv def create_jwt(private_key, app_id): """ @@ -12,7 +13,7 @@ def create_jwt(private_key, app_id): :return: """ # Open PEM - # with open(pem_path, 'rb') as pem_file: + # with open(private_key, 'rb') as pem_file: # signing_key = jwk_from_pem(pem_file.read()) signing_key = jwk_from_pem(private_key.encode('utf-8')) @@ -30,27 +31,82 @@ def create_jwt(private_key, app_id): encoded_jwt = jwt_instance.encode(payload, signing_key, alg='RS256') # Set JWT as environment variable - os.environ["GITHUB_JWT"] = encoded_jwt + # os.environ["GITHUB_JWT"] = encoded_jwt - print(f"JWT set as environment variable: JWT={encoded_jwt}") + # print(f"JWT token created successfully") return encoded_jwt +def get_app_installation_id(jwt:str, github_account_type:str): + """ + returns github app installation id on user and org accounts + :param jwt: + :return: + """ + GITHUB_REPOSITORY = os.getenv('GITHUB_REPOSITORY') + GITHUB_REPOSITORY_OWNER = os.getenv('GITHUB_REPOSITORY_OWNER') + org_url = f'https://api.github.com/repos/{GITHUB_REPOSITORY}/installation' + user_url = f'https://api.github.com/users/{GITHUB_REPOSITORY_OWNER}/installation' + if github_account_type == 'user': + url = user_url + else: + url = org_url + headers = { + "Accept": "application/vnd.github+json", + "Authorization": f"Bearer {jwt}", + "X-GitHub-Api-Version": "2022-11-28" + } + response = requests.get(url= url, headers=headers) + + if response.status_code == 200: + print(f'Okay. Received proper response.Got installation id') + response_json = response.json() + elif response.status_code == 301: + print(f'Moved permanently. Cant get a response') + else: + print(f'Resource Not Found!') + + # Installation id of github app + installation_id = response_json['id'] + return installation_id + +def generate_token_by_post_call(installation_id:int, jwt:str): + """ + create a app installation token by doing a rest api post call with permissions for application + :return: + """ + url = f'https://api.github.com/app/installations/{installation_id}/access_tokens' + headers = { + "Accept": "application/vnd.github+json", + "Authorization": f"Bearer {jwt}", + "X-GitHub-Api-Version": "2022-11-28" + } + response = requests.post(url=url, headers=headers) + response_json = response.json() + if response.status_code == 201: + print(f'Github app installation token generate succcessfully, expires at {response_json["expires_at"]}') + os.environ['GH_TOKEN'] = response_json['token'] def main(): """ to test the code :return: """ + load_dotenv() parser = argparse.ArgumentParser(description="Create JWT for GitHub App authentication") parser.add_argument("--github_app_private_key",required=True, type=str, help="Github App Private key") + parser.add_argument("--github_account_type",required=True, choices=['user','organization'], help="Github account whether user account ot github org") parser.add_argument("--github_app_id",required=True, type=str, help="Your GitHub App ID") args = parser.parse_args() private_key = args.github_app_private_key app_id = args.github_app_id + github_account_type = args.github_account_type # function call - create_jwt(private_key, app_id) + jwt = create_jwt(private_key=private_key, app_id=app_id) + installation_id = get_app_installation_id(jwt=jwt, github_account_type=github_account_type) + generate_token_by_post_call(installation_id=installation_id, jwt=jwt) + if __name__ == "__main__": main()