Skip to content

Commit b321687

Browse files
feat: use fine-grained token for organization membership checking
Switch from GITHUB_TOKEN to fine-grained token with membership read permissions: - Resolves private membership visibility issues - Uses MACHINE_USER_CORE_ORG_MEMBERSHIP_CHECK fine-grained token - Enables checking both public and private organization members - Updates action to accept github_token input parameter - Updates workflow to pass fine-grained token secret to action - Updates documentation with fine-grained token requirements This should now correctly authorize sfreudenthaler and other private members. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent 19a08fd commit b321687

File tree

3 files changed

+34
-18
lines changed

3 files changed

+34
-18
lines changed

.github/actions/security/org-membership-check/README.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ This composite action checks if a GitHub user is a member of the dotCMS organiza
1313
| Input | Description | Required | Default |
1414
|-------|-------------|----------|---------|
1515
| `username` | GitHub username to check | Yes | N/A |
16+
| `github_token` | Fine-grained GitHub token with organization membership read permissions | Yes | N/A |
1617

1718
## Outputs
1819

@@ -29,6 +30,7 @@ This composite action checks if a GitHub user is a member of the dotCMS organiza
2930
uses: ./.github/actions/security/org-membership-check
3031
with:
3132
username: ${{ github.actor }}
33+
github_token: ${{ secrets.MACHINE_USER_CORE_ORG_MEMBERSHIP_CHECK }}
3234

3335
- name: Conditional step based on membership
3436
if: steps.membership-check.outputs.is_member == 'true'
@@ -37,7 +39,12 @@ This composite action checks if a GitHub user is a member of the dotCMS organiza
3739
3840
## Implementation Details
3941
40-
The action uses the GitHub CLI (`gh`) with the repository's `GITHUB_TOKEN` to check organization membership via the GitHub API endpoint `GET /orgs/dotCMS/members/{username}`.
42+
The action uses the GitHub CLI (`gh`) with a fine-grained GitHub token to check organization membership via the GitHub API endpoint `GET /orgs/dotCMS/members/{username}`.
43+
44+
**Token Requirements:**
45+
- Fine-grained token with organization membership read permissions
46+
- Should be from a machine/service account for security
47+
- Stored as repository secret: `MACHINE_USER_CORE_ORG_MEMBERSHIP_CHECK`
4148

4249
**Key Design Decision: Status Code vs Response Body**
4350

@@ -57,4 +64,5 @@ This approach correctly authorizes all organization members (including owners wi
5764
- Only checks membership in the dotCMS organization (hardcoded)
5865
- Does not expose whether membership is public or private
5966
- Logs authorization results without sensitive details
60-
- Uses repository's built-in `GITHUB_TOKEN` for API access
67+
- Uses fine-grained token with minimal required permissions (organization membership read)
68+
- Token should be regularly rotated and monitored for usage

.github/actions/security/org-membership-check/action.yml

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ inputs:
55
username:
66
description: 'GitHub username to check'
77
required: true
8+
github_token:
9+
description: 'Fine-grained GitHub token with organization membership read permissions'
10+
required: true
811

912
outputs:
1013
is_member:
@@ -24,31 +27,30 @@ runs:
2427
echo "Checking organization membership for user: ${{ inputs.username }} in dotCMS organization"
2528
2629
# Use GitHub CLI to check organization membership
27-
# This uses the GITHUB_TOKEN which has read access to organization membership
30+
# This uses a fine-grained token with organization membership read permissions
2831
2932
set +e # Don't exit on error, we want to handle it gracefully
3033
31-
# Check organization membership using GitHub API
32-
#
33-
# IMPORTANT: We check the HTTP status code (via exit code) rather than parsing
34-
# the response body because GitHub's membership API has specific behavior:
34+
# Check organization membership using GitHub API with fine-grained token
3535
#
36-
# HTTP 200 (exit code 0) = User IS a member (regardless of response content)
37-
# - Public member: Returns user object with populated fields
38-
# - Private member: Returns empty response body (but still 200 OK)
36+
# Uses a fine-grained token with organization membership read permissions
37+
# to check both public and private organization membership reliably.
3938
#
40-
# HTTP 404 (exit code 1) = User is NOT a member
41-
# - Returns {"message":"Not Found",...} error response
42-
#
43-
# This approach correctly handles both public and private organization members
44-
# without needing to distinguish between their visibility settings.
39+
# API Behavior:
40+
# - HTTP 200 + user object = Public member
41+
# - HTTP 200 + empty response = Private member
42+
# - HTTP 404 = Not a member
43+
# - Other errors = API/token issues
44+
45+
echo "Checking organization membership for ${{ inputs.username }} in dotCMS..."
4546
46-
response=$(gh api orgs/dotCMS/members/${{ inputs.username }} 2>/dev/null)
47+
# Use the provided fine-grained token instead of default GITHUB_TOKEN
48+
response=$(gh api orgs/dotCMS/members/${{ inputs.username }} \
49+
--header "Authorization: token ${{ inputs.github_token }}" 2>/dev/null)
4750
api_exit_code=$?
4851
4952
if [ $api_exit_code -eq 0 ]; then
5053
# HTTP 200: User is a member (public or private)
51-
# We check response content only for logging clarity, not authorization logic
5254
if [ -n "$response" ]; then
5355
echo "✅ User ${{ inputs.username }} is a member of dotCMS (public membership)"
5456
else
@@ -57,7 +59,7 @@ runs:
5759
echo "is_member=true" >> $GITHUB_OUTPUT
5860
echo "membership_status=member" >> $GITHUB_OUTPUT
5961
else
60-
# HTTP 404 or other error: User is not a member
62+
# HTTP 404 or other error: User is not a member or API issue
6163
echo "❌ User ${{ inputs.username }} is not a member of dotCMS (API exit code: $api_exit_code)"
6264
echo "is_member=false" >> $GITHUB_OUTPUT
6365
echo "membership_status=non-member" >> $GITHUB_OUTPUT

.github/workflows/issue_comment_claude-code-review.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ jobs:
2222
# Security gate: Check if user is dotCMS organization member
2323
security-check:
2424
runs-on: ubuntu-latest
25+
permissions:
26+
contents: read # Allow repository checkout
27+
metadata: read # Allow reading repository metadata
28+
# Note: Organization membership checking uses the actor's permissions
29+
# not the GITHUB_TOKEN, so no additional permissions needed for that API
2530
outputs:
2631
authorized: ${{ steps.membership-check.outputs.is_member }}
2732
steps:
@@ -33,6 +38,7 @@ jobs:
3338
uses: ./.github/actions/security/org-membership-check
3439
with:
3540
username: ${{ github.event.comment.user.login || github.actor }}
41+
github_token: ${{ secrets.MACHINE_USER_CORE_ORG_MEMBERSHIP_CHECK }}
3642

3743
- name: Log security decision
3844
run: |

0 commit comments

Comments
 (0)