Skip to content

Commit bee5d09

Browse files
Authenticate CI via GitHub OIDC and AWS Secrets Manager
Migrate CI credential handling to GitHub OIDC + AWS Secrets Manager, mirroring dropbox-sdk-dotnet: credential-consuming jobs assume an AWS IAM role and fetch the api-sdk-integration-test-creds and codecov-token secrets, replacing long-lived GitHub secrets. Credential-gated jobs are skipped on fork PRs, which cannot assume the role. Also stop injecting the unused legacy user token and authenticate the team integration fixture with a refresh token instead of a static token.
1 parent eee36cb commit bee5d09

3 files changed

Lines changed: 79 additions & 30 deletions

File tree

.github/workflows/ci.yml

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,13 @@ jobs:
9393
sphinx-build -b html docs build/html
9494
9595
Integration:
96+
# Skip on fork PRs: they cannot assume the OIDC role.
97+
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
9698
continue-on-error: true
9799
runs-on: ${{ matrix.os }}
100+
permissions: # required for OIDC
101+
id-token: write
102+
contents: read
98103
strategy:
99104
fail-fast: false
100105
max-parallel: 1
@@ -126,19 +131,27 @@ jobs:
126131
run: |
127132
python -m pip install "$(python -c "import glob,sys; w=glob.glob('dist/*.whl'); assert w, f'No wheel built. dist contains: {glob.glob(\"dist/*\")}'; print(w[0])")"
128133
134+
- name: Configure AWS credentials (OIDC)
135+
uses: aws-actions/configure-aws-credentials@v6
136+
with:
137+
role-to-assume: arn:aws:iam::082972943155:role/oidc-github-dropbox-dropbox-sdk-python-repo
138+
aws-region: us-west-2
139+
140+
- name: Get integration credentials from AWS Secrets Manager
141+
uses: aws-actions/aws-secretsmanager-get-secrets@v3
142+
with:
143+
secret-ids: |
144+
CREDS,api-sdk-integration-test-creds
145+
parse-json-secrets: true
146+
129147
- name: Run integration tests
130148
env:
131-
LEGACY_USER_DROPBOX_TOKEN: ${{ secrets.LEGACY_USER_DROPBOX_TOKEN }}
132-
LEGACY_USER_CLIENT_ID: ${{ secrets.LEGACY_USER_CLIENT_ID }}
133-
LEGACY_USER_CLIENT_SECRET: ${{ secrets.LEGACY_USER_CLIENT_SECRET }}
134-
LEGACY_USER_REFRESH_TOKEN: ${{ secrets.LEGACY_USER_REFRESH_TOKEN }}
135-
SCOPED_USER_CLIENT_ID: ${{ secrets.SCOPED_USER_CLIENT_ID }}
136-
SCOPED_USER_CLIENT_SECRET: ${{ secrets.SCOPED_USER_CLIENT_SECRET }}
137-
SCOPED_USER_REFRESH_TOKEN: ${{ secrets.SCOPED_USER_REFRESH_TOKEN }}
138-
SCOPED_TEAM_DROPBOX_TOKEN: ${{ secrets.SCOPED_TEAM_DROPBOX_TOKEN }}
139-
SCOPED_TEAM_CLIENT_ID: ${{ secrets.SCOPED_TEAM_CLIENT_ID }}
140-
SCOPED_TEAM_CLIENT_SECRET: ${{ secrets.SCOPED_TEAM_CLIENT_SECRET }}
141-
SCOPED_TEAM_REFRESH_TOKEN: ${{ secrets.SCOPED_TEAM_REFRESH_TOKEN }}
142-
DROPBOX_SHARED_LINK: ${{ secrets.DROPBOX_SHARED_LINK }}
149+
SCOPED_USER_CLIENT_ID: ${{ env.CREDS_SCOPED_USER_CLIENT_ID }}
150+
SCOPED_USER_CLIENT_SECRET: ${{ env.CREDS_SCOPED_USER_CLIENT_SECRET }}
151+
SCOPED_USER_REFRESH_TOKEN: ${{ env.CREDS_SCOPED_USER_REFRESH_TOKEN }}
152+
SCOPED_TEAM_CLIENT_ID: ${{ env.CREDS_SCOPED_TEAM_CLIENT_ID }}
153+
SCOPED_TEAM_CLIENT_SECRET: ${{ env.CREDS_SCOPED_TEAM_CLIENT_SECRET }}
154+
SCOPED_TEAM_REFRESH_TOKEN: ${{ env.CREDS_SCOPED_TEAM_REFRESH_TOKEN }}
155+
DROPBOX_SHARED_LINK: ${{ env.CREDS_DROPBOX_SHARED_LINK }}
143156
run: |
144157
pytest -v test/integration/test_dropbox.py

.github/workflows/coverage.yml

Lines changed: 45 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ on:
1010
jobs:
1111
Coverage:
1212
runs-on: ubuntu-latest
13+
permissions: # required for OIDC
14+
id-token: write
15+
contents: read
1316
steps:
1417
- uses: actions/checkout@v7
1518
- name: Setup Python environment
@@ -32,14 +35,34 @@ jobs:
3235
run: |
3336
coverage run --rcfile=.coveragerc -m pytest test/unit/
3437
coverage xml
38+
# Upload steps gated to same-repo runs: fork PRs cannot assume the role.
39+
- name: Configure AWS credentials (OIDC)
40+
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
41+
uses: aws-actions/configure-aws-credentials@v6
42+
with:
43+
role-to-assume: arn:aws:iam::082972943155:role/oidc-github-dropbox-dropbox-sdk-python-repo
44+
aws-region: us-west-2
45+
- name: Get Codecov token from AWS Secrets Manager
46+
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
47+
uses: aws-actions/aws-secretsmanager-get-secrets@v3
48+
with:
49+
secret-ids: |
50+
CODECOV_TOKEN,codecov-token-dropbox-sdk-python
51+
parse-json-secrets: false
3552
- name: Publish Coverage
3653
uses: codecov/codecov-action@v7
54+
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
3755
with:
38-
token: ${{ secrets.CODECOV_TOKEN }}
56+
token: ${{ env.CODECOV_TOKEN }}
3957
flags: unit
4058
fail_ci_if_error: true
4159
IntegrationCoverage:
60+
# Skip on fork PRs: they cannot assume the OIDC role.
61+
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
4262
runs-on: ubuntu-latest
63+
permissions: # required for OIDC
64+
id-token: write
65+
contents: read
4366
steps:
4467
- uses: actions/checkout@v7
4568
- name: Setup Python environment
@@ -58,26 +81,33 @@ jobs:
5881
pip install -r requirements.txt
5982
pip install -r test/requirements.txt
6083
pip install .
61-
- name: Generate Unit Test Coverage
84+
- name: Configure AWS credentials (OIDC)
85+
uses: aws-actions/configure-aws-credentials@v6
86+
with:
87+
role-to-assume: arn:aws:iam::082972943155:role/oidc-github-dropbox-dropbox-sdk-python-repo
88+
aws-region: us-west-2
89+
- name: Get integration credentials from AWS Secrets Manager
90+
uses: aws-actions/aws-secretsmanager-get-secrets@v3
91+
with:
92+
secret-ids: |
93+
CREDS,api-sdk-integration-test-creds
94+
CODECOV_TOKEN,codecov-token-dropbox-sdk-python
95+
parse-json-secrets: true
96+
- name: Generate Coverage
6297
env:
63-
LEGACY_USER_DROPBOX_TOKEN: ${{ secrets.LEGACY_USER_DROPBOX_TOKEN }}
64-
LEGACY_USER_CLIENT_ID: ${{ secrets.LEGACY_USER_CLIENT_ID }}
65-
LEGACY_USER_CLIENT_SECRET: ${{ secrets.LEGACY_USER_CLIENT_SECRET }}
66-
LEGACY_USER_REFRESH_TOKEN: ${{ secrets.LEGACY_USER_REFRESH_TOKEN }}
67-
SCOPED_USER_CLIENT_ID: ${{ secrets.SCOPED_USER_CLIENT_ID }}
68-
SCOPED_USER_CLIENT_SECRET: ${{ secrets.SCOPED_USER_CLIENT_SECRET }}
69-
SCOPED_USER_REFRESH_TOKEN: ${{ secrets.SCOPED_USER_REFRESH_TOKEN }}
70-
SCOPED_TEAM_DROPBOX_TOKEN: ${{ secrets.SCOPED_TEAM_DROPBOX_TOKEN }}
71-
SCOPED_TEAM_CLIENT_ID: ${{ secrets.SCOPED_TEAM_CLIENT_ID }}
72-
SCOPED_TEAM_CLIENT_SECRET: ${{ secrets.SCOPED_TEAM_CLIENT_SECRET }}
73-
SCOPED_TEAM_REFRESH_TOKEN: ${{ secrets.SCOPED_TEAM_REFRESH_TOKEN }}
74-
DROPBOX_SHARED_LINK: ${{ secrets.DROPBOX_SHARED_LINK }}
98+
SCOPED_USER_CLIENT_ID: ${{ env.CREDS_SCOPED_USER_CLIENT_ID }}
99+
SCOPED_USER_CLIENT_SECRET: ${{ env.CREDS_SCOPED_USER_CLIENT_SECRET }}
100+
SCOPED_USER_REFRESH_TOKEN: ${{ env.CREDS_SCOPED_USER_REFRESH_TOKEN }}
101+
SCOPED_TEAM_CLIENT_ID: ${{ env.CREDS_SCOPED_TEAM_CLIENT_ID }}
102+
SCOPED_TEAM_CLIENT_SECRET: ${{ env.CREDS_SCOPED_TEAM_CLIENT_SECRET }}
103+
SCOPED_TEAM_REFRESH_TOKEN: ${{ env.CREDS_SCOPED_TEAM_REFRESH_TOKEN }}
104+
DROPBOX_SHARED_LINK: ${{ env.CREDS_DROPBOX_SHARED_LINK }}
75105
run: |
76106
coverage run --rcfile=.coveragerc -m pytest test/integration/test_dropbox.py
77107
coverage xml
78108
- name: Publish Coverage
79109
uses: codecov/codecov-action@v7
80110
with:
81-
token: ${{ secrets.CODECOV_TOKEN }}
111+
token: ${{ env.CODECOV_TOKEN }}
82112
flags: integration
83113
fail_ci_if_error: true

test/integration/test_dropbox.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,9 +114,15 @@ def bad_secret_dbx_from_env(dbx_session):
114114

115115
@pytest.fixture()
116116
def dbx_team_from_env(dbx_session):
117-
team_oauth2_token = _value_from_env_or_die(
118-
format_env_name(SCOPED_KEY, TEAM_KEY, ACCESS_TOKEN_KEY))
119-
return DropboxTeam(team_oauth2_token, session=dbx_session)
117+
refresh_token = _value_from_env_or_die(
118+
format_env_name(SCOPED_KEY, TEAM_KEY, REFRESH_TOKEN_KEY))
119+
app_key = _value_from_env_or_die(
120+
format_env_name(SCOPED_KEY, TEAM_KEY, CLIENT_ID_KEY))
121+
app_secret = _value_from_env_or_die(
122+
format_env_name(SCOPED_KEY, TEAM_KEY, CLIENT_SECRET_KEY))
123+
return DropboxTeam(oauth2_refresh_token=refresh_token,
124+
app_key=app_key, app_secret=app_secret,
125+
session=dbx_session)
120126

121127

122128
@pytest.fixture()

0 commit comments

Comments
 (0)