|
| 1 | +#!/bin/bash |
| 2 | + |
| 3 | +# Check if a user is an enterprise admin |
| 4 | +# API: https://docs.github.com/en/enterprise-cloud@latest/graphql/reference/objects#enterpriseownerinfo |
| 5 | +# Reference: https://docs.github.com/en/enterprise-cloud@latest/graphql/reference/queries#enterprise |
| 6 | + |
| 7 | +# Usage: ./check-enterprise-owner.sh <ENTERPRISE_SLUG> <USERNAME> |
| 8 | +# Example: ./check-enterprise-owner.sh octocat johndoe |
| 9 | + |
| 10 | +set -e |
| 11 | + |
| 12 | +# Check if required parameters are provided |
| 13 | +if [ $# -lt 2 ]; then |
| 14 | + echo "Usage: $0 <ENTERPRISE_SLUG> <USERNAME>" |
| 15 | + echo "Example: $0 octocat johndoe" |
| 16 | + echo "" |
| 17 | + echo "Note: Requires 'gh' CLI to be installed and authenticated" |
| 18 | + exit 1 |
| 19 | +fi |
| 20 | + |
| 21 | +ENTERPRISE_SLUG="$1" |
| 22 | +TARGET_USER="$2" |
| 23 | + |
| 24 | +# Check if gh CLI is available |
| 25 | +if ! command -v gh &> /dev/null; then |
| 26 | + echo "Error: GitHub CLI (gh) is required but not installed." |
| 27 | + echo "Install it from: https://cli.github.com/" |
| 28 | + exit 1 |
| 29 | +fi |
| 30 | + |
| 31 | +# Check if gh is authenticated |
| 32 | +if ! gh auth status &> /dev/null; then |
| 33 | + echo "Error: GitHub CLI is not authenticated." |
| 34 | + echo "Run 'gh auth login' to authenticate." |
| 35 | + exit 1 |
| 36 | +fi |
| 37 | + |
| 38 | +# Check if jq is available for JSON parsing |
| 39 | +if ! command -v jq &> /dev/null; then |
| 40 | + echo "Error: jq is required for JSON parsing but not installed." |
| 41 | + echo "Install it with: brew install jq (on macOS) or your package manager" |
| 42 | + exit 1 |
| 43 | +fi |
| 44 | + |
| 45 | +USER_FOUND=false |
| 46 | +CURSOR="" |
| 47 | +HAS_NEXT_PAGE=true |
| 48 | + |
| 49 | +echo "Checking if user '$TARGET_USER' is an enterprise admin for '$ENTERPRISE_SLUG'..." |
| 50 | + |
| 51 | +while [ "$HAS_NEXT_PAGE" = "true" ]; do |
| 52 | + # Build the GraphQL query with optional cursor |
| 53 | + if [ -z "$CURSOR" ]; then |
| 54 | + QUERY=' |
| 55 | + query CheckEnterpriseAdmin($slug: String!) { |
| 56 | + enterprise(slug: $slug) { |
| 57 | + id |
| 58 | + name |
| 59 | + slug |
| 60 | + ownerInfo { |
| 61 | + admins(first: 100) { |
| 62 | + totalCount |
| 63 | + pageInfo { |
| 64 | + hasNextPage |
| 65 | + endCursor |
| 66 | + } |
| 67 | + nodes { |
| 68 | + login |
| 69 | + name |
| 70 | + } |
| 71 | + } |
| 72 | + } |
| 73 | + } |
| 74 | + }' |
| 75 | + RESPONSE=$(gh api graphql -f query="$QUERY" -f slug="$ENTERPRISE_SLUG" 2>/dev/null) |
| 76 | + else |
| 77 | + QUERY=' |
| 78 | + query CheckEnterpriseAdmin($slug: String!, $cursor: String!) { |
| 79 | + enterprise(slug: $slug) { |
| 80 | + id |
| 81 | + name |
| 82 | + slug |
| 83 | + ownerInfo { |
| 84 | + admins(first: 100, after: $cursor) { |
| 85 | + totalCount |
| 86 | + pageInfo { |
| 87 | + hasNextPage |
| 88 | + endCursor |
| 89 | + } |
| 90 | + nodes { |
| 91 | + login |
| 92 | + name |
| 93 | + } |
| 94 | + } |
| 95 | + } |
| 96 | + } |
| 97 | + }' |
| 98 | + RESPONSE=$(gh api graphql -f query="$QUERY" -f slug="$ENTERPRISE_SLUG" -f cursor="$CURSOR" 2>/dev/null) |
| 99 | + fi |
| 100 | + |
| 101 | + # Check for errors in the response |
| 102 | + if [ $? -ne 0 ] || echo "$RESPONSE" | jq -e '.errors' > /dev/null 2>&1; then |
| 103 | + ERROR_MSG=$(echo "$RESPONSE" | jq -r '.errors[0].message // "Unknown error"' 2>/dev/null || echo "API call failed") |
| 104 | + echo "✗ Error accessing enterprise '$ENTERPRISE_SLUG': $ERROR_MSG" |
| 105 | + |
| 106 | + # Check for common error types |
| 107 | + if echo "$ERROR_MSG" | grep -q "Could not resolve to an Enterprise"; then |
| 108 | + echo " Enterprise slug '$ENTERPRISE_SLUG' not found or not accessible" |
| 109 | + elif echo "$ERROR_MSG" | grep -q "Must have admin access"; then |
| 110 | + echo " Current user does not have admin access to view enterprise admins" |
| 111 | + fi |
| 112 | + exit 1 |
| 113 | + fi |
| 114 | + |
| 115 | + # Check if enterprise data exists |
| 116 | + if ! echo "$RESPONSE" | jq -e '.data.enterprise' > /dev/null 2>&1; then |
| 117 | + echo "✗ Enterprise '$ENTERPRISE_SLUG' not found or not accessible" |
| 118 | + exit 1 |
| 119 | + fi |
| 120 | + |
| 121 | + # Extract data from response |
| 122 | + TOTAL_COUNT=$(echo "$RESPONSE" | jq -r '.data.enterprise.ownerInfo.admins.totalCount') |
| 123 | + HAS_NEXT_PAGE=$(echo "$RESPONSE" | jq -r '.data.enterprise.ownerInfo.admins.pageInfo.hasNextPage') |
| 124 | + CURSOR=$(echo "$RESPONSE" | jq -r '.data.enterprise.ownerInfo.admins.pageInfo.endCursor') |
| 125 | + |
| 126 | + # Check if target user is in current page |
| 127 | + ADMINS=$(echo "$RESPONSE" | jq -r '.data.enterprise.ownerInfo.admins.nodes[].login') |
| 128 | + |
| 129 | + PAGE_COUNT=$(echo "$RESPONSE" | jq -r '.data.enterprise.ownerInfo.admins.nodes | length') |
| 130 | + echo "Checking page with $PAGE_COUNT admins..." |
| 131 | + |
| 132 | + for admin in $ADMINS; do |
| 133 | + # Case-insensitive comparison |
| 134 | + if [ "$(echo "$admin" | tr '[:upper:]' '[:lower:]')" = "$(echo "$TARGET_USER" | tr '[:upper:]' '[:lower:]')" ]; then |
| 135 | + USER_FOUND=true |
| 136 | + echo "✓ User '$TARGET_USER' found as enterprise admin!" |
| 137 | + |
| 138 | + # Get full admin details |
| 139 | + ADMIN_NAME=$(echo "$RESPONSE" | jq -r --arg login "$admin" '.data.enterprise.ownerInfo.admins.nodes[] | select(.login == $login) | .name') |
| 140 | + echo " Login: $admin" |
| 141 | + echo " Name: $ADMIN_NAME" |
| 142 | + break 2 # Break out of both loops |
| 143 | + fi |
| 144 | + done |
| 145 | + |
| 146 | + # If no next page, break |
| 147 | + if [ "$HAS_NEXT_PAGE" = "false" ]; then |
| 148 | + break |
| 149 | + fi |
| 150 | +done |
| 151 | + |
| 152 | +# Final result |
| 153 | +echo "" |
| 154 | +echo "=== SUMMARY ===" |
| 155 | +echo "Enterprise: $ENTERPRISE_SLUG" |
| 156 | +echo "Total admins checked: $TOTAL_COUNT" |
| 157 | +if [ "$USER_FOUND" = "true" ]; then |
| 158 | + echo "Result: ✓ '$TARGET_USER' IS an enterprise admin" |
| 159 | + exit 0 |
| 160 | +else |
| 161 | + echo "Result: ✗ '$TARGET_USER' is NOT an enterprise admin" |
| 162 | + exit 1 |
| 163 | +fi |
0 commit comments