Skip to content

Instantly share code, notes, and snippets.

@timbirk
Last active August 9, 2018 13:23
Show Gist options
  • Save timbirk/ff0eca4a4de47afee5ce6542a0fe2394 to your computer and use it in GitHub Desktop.
Save timbirk/ff0eca4a4de47afee5ce6542a0fe2394 to your computer and use it in GitHub Desktop.
Checks and optionally sets a Github user or teams permission on matching repos in an org
#!/usr/bin/env ruby
require 'octokit'
require 'optparse'
require 'terminal-table'
require 'io/console'
def yesno
case $stdin.getch.downcase
when "Y".downcase then true
when "N".downcase then false
else raise "Invalid character."
end
end
options = {}
optparse = OptionParser.new do |opts|
opts.banner = "Usage: #{__FILE__} [options]"
opts.on("-o", "--org ORGANIZATION", "Github Organization.") do |o|
options[:organization] = o
end
opts.on("-r", "--repo REPOPATTERN", "Repository name pattern to match.",
" Accepts regex, example: \"tf-aws-.*\"") do |r|
options[:repopattern] = r
end
opts.on("-u", "--username [USERNAME]", "Username to check access for. Optional.") do |u|
options[:username] = u
end
opts.on("-t", "--team [TEAM]", "Team to check access for. Supercedes username. Optional.") do |t|
options[:team] = t
end
opts.on("-s", "--set [none, read or write]",
"Permission to set for the user on matching repos. Optional.") do |s|
case s
when 'none', 'read', 'write'
options[:permission] = s
else
raise OptionParser::InvalidOption.new(Reason="#{s}, permission must be none, read or write")
end
end
opts.on('-h', '--help', 'Display this screen') do
puts opts
exit
end
end
begin
optparse.parse!
mandatory = [:organization, :repopattern]
missing = mandatory.select{ |param| options[param].nil? }
unless missing.empty?
raise OptionParser::MissingArgument.new(missing.join(', '))
end
rescue OptionParser::InvalidOption, OptionParser::MissingArgument
puts $!.to_s
puts optparse
exit
end
@client = Octokit::Client.new(:netrc => true, :per_page => 100,
:auto_traversal => true, :auto_paginate => true)
@client.login
current_user = @client.user()
if options[:username].nil?
options[:username] = current_user[:login]
end
if options[:team]
teams = @client.organization_teams(options[:organization])
team = teams.find{ |team| team[:name] == options[:team] }
if team.nil?
puts "ERROR: Team #{options[:team]} cannot be found in the #{options[:organization]} organization"
exit 1
end
end
puts "Getting repos matching #{options[:repopattern]} for #{options[:organization]} organization..."
repos = @client.org_repos(options[:organization].to_s, {:type => 'all'}).select {
|repo| (/#{options[:repopattern]}/ =~ repo[:full_name].split('/')[1])
}
if repos.empty?
puts "ERROR: No repos found matching #{options[:repopattern]}"
exit 1
end
if team
options[:team_id] = team[:id]
puts "Getting repos matching #{options[:repopattern]} for #{options[:team]} team..."
team_repos = @client.team_repositories(options[:team_id]).select {
|repo| (/#{options[:repopattern]}/ =~ repo[:full_name].split('/')[1])
}
end
puts "Checking permissions for: #{options[:team] || options[:username]} against #{repos.length} repositories..."
rows = []
repos_to_fix = []
repos.each do |repo|
if options[:team_id]
repo_perms = {}
repo_perms[:permission] = 'none'
team_repo = team_repos.find{ |arepo| arepo[:full_name] == repo[:full_name] }
unless team_repo.nil?
if team_repo[:permissions][:pull]
repo_perms[:permission] = 'read'
if team_repo[:permissions][:push]
repo_perms[:permission] = 'write'
if team_repo[:permissions][:admin]
repo_perms[:permission] = 'admin'
end
end
end
end
else
repo_perms = @client.permission_level(repo[:full_name], options[:username], :accept => "application/vnd.github.beta+json")
end
row = [repo[:full_name], repo_perms[:permission]]
if options[:permission]
row << options[:permission]
if repo_perms[:permission] != options[:permission]
repos_to_fix << repo[:full_name]
end
end
rows << row
end
headings = ['Repo', 'Permission']
if options[:permission]
headings << 'New Permission'
end
table = Terminal::Table.new :headings => headings, :rows => rows
puts table
if options[:permission] && repos_to_fix.any?
puts "Are you sure you want to set the permissions for #{options[:team] || options[:username]} to #{options[:permission]} on #{repos_to_fix.length} repos? Y/N"
if yesno
for repo in repos_to_fix
puts "Setting permissions on #{repo}..."
case options[:permission]
when "read"
perm = "pull"
when "write"
perm = "push"
else
perm = "none"
end
if options[:team]
if perm == "none"
@client.remove_team_repository(options[:team_id], repo)
else
@client.add_team_repository(options[:team_id], repo, :permission => perm)
end
else
if perm == "none"
@client.remove_collaborator(repo, options[:username])
else
@client.add_collaborator(repo, options[:username], :permission => 'pull')
end
end
end
puts "Done"
else
puts "Exiting"
end
end
@timbirk
Copy link
Author

timbirk commented Jul 30, 2018

Requires octokit and terminal-table gems. Also .netrc setup for Github access.

@timbirk
Copy link
Author

timbirk commented Aug 9, 2018

Example usage:

$ ./ghperms -h
Usage: ./ghperms [options]
    -o, --org ORGANIZATION           Github Organization.
    -r, --repo REPOPATTERN           Repository name pattern to match.
                                       Accepts regex, example: "tf-aws-.*"
    -u, --username [USERNAME]        Username to check access for. Optional.
    -t, --team [TEAM]                Team to check access for. Supercedes username. Optional.
    -s, --set [none, read or write]  Permission to set for the user on matching repos. Optional.
    -h, --help                       Display this screen

Showing current user (me) permissions on a repo:

$ ./ghperms -o itv -r talpay-infra
Getting repos matching talpay-infra for itv organization...
Checking permissions for: timbirk against 1 repositories...
+------------------+------------+
| Repo             | Permission |
+------------------+------------+
| ITV/talpay-infra | admin      |
+------------------+------------+

Checking repo access for a specified user:

$ ./ghperms -o itv -r talpay-infra -u abegum
Getting repos matching talpay-infra for itv organization...
Checking permissions for: abegum against 1 repositories...
+------------------+------------+
| Repo             | Permission |
+------------------+------------+
| ITV/talpay-infra | none       |
+------------------+------------+

Check permissions for a team:

$ ./ghperms -o itv -r talpay-infra -t itv-ci
Getting repos matching talpay-infra for itv organization...
Getting repos matching talpay-infra for itv-ci team...
Checking permissions for: itv-ci against 1 repositories...
+------------------+------------+
| Repo             | Permission |
+------------------+------------+
| ITV/talpay-infra | read       |
+------------------+------------+

Setting permissions is easy with the -s or --set parameter:

$ ./ghperms -o itv -r talpay-infra -t itv-ci -s write
Getting repos matching talpay-infra for itv organization...
Getting repos matching talpay-infra for itv-ci team...
Checking permissions for: itv-ci against 1 repositories...
+------------------+------------+----------------+
| Repo             | Permission | New Permission |
+------------------+------------+----------------+
| ITV/talpay-infra | read       | write          |
+------------------+------------+----------------+
Are you sure you want to set the permissions for itv-ci to write on 1 repos? Y/N
Setting permissions on ITV/talpay-infra...
Done
$ ./ghperms -o itv -r talpay-infra -t itv-ci -s read
Getting repos matching talpay-infra for itv organization...
Getting repos matching talpay-infra for itv-ci team...
Checking permissions for: itv-ci against 1 repositories...
+------------------+------------+----------------+
| Repo             | Permission | New Permission |
+------------------+------------+----------------+
| ITV/talpay-infra | write      | read           |
+------------------+------------+----------------+
Are you sure you want to set the permissions for itv-ci to read on 1 repos? Y/N
Setting permissions on ITV/talpay-infra...
Done

Using regex patterns:

$ ./ghperms -o itv -r "^[\w]+-infra$" -t itv-ci
Getting repos matching ^[\w]+-infra$ for itv organization...
Getting repos matching ^[\w]+-infra$ for itv-ci team...
Checking permissions for: itv-ci against 24 repositories...
+--------------------+------------+
| Repo               | Permission |
+--------------------+------------+
| ITV/user-infra     | none       |
| ITV/site-infra     | none       |
| ITV/talpay-infra   | read       |
| ITV/hubsvc-infra   | none       |
| ITV/ats-infra      | none       |
| ITV/phoenix-infra  | none       |
| ITV/10ft-infra     | none       |
| ITV/oasvc-infra    | none       |
| ITV/core-infra     | read       |
| ITV/fp-infra       | none       |
| ITV/root-infra     | none       |
| ITV/cosmos-infra   | read       |
| ITV/caspa-infra    | none       |
| ITV/mp-infra       | none       |
| ITV/bs-infra       | read       |
| ITV/cm-infra       | read       |
| ITV/rmcs-infra     | read       |
| ITV/browser-infra  | none       |
| ITV/kronos-infra   | none       |
| ITV/selfserv-infra | none       |
| ITV/tp-infra       | read       |
| ITV/cp-infra       | read       |
| ITV/cd-infra       | none       |
| ITV/wincp-infra    | write      |
+--------------------+------------+
$ ./ghperms -o itv -r "tf-aws-.*" -t itv-ci
Getting repos matching tf-aws-.* for itv organization...
Getting repos matching tf-aws-.* for itv-ci team...
Checking permissions for: itv-ci against 40 repositories...
+----------------------------+------------+
| Repo                       | Permission |
+----------------------------+------------+
| ITV/tf-aws-vpc             | read       |
| ITV/tf-aws-sg              | read       |
| ITV/tf-aws-public-subnet   | read       |
| ITV/tf-aws-private-subnet  | read       |
| ITV/tf-aws-asg             | read       |
| ITV/tf-aws-iam             | read       |
| ITV/tf-aws-asg-elb         | read       |
| ITV/tf-aws-route53         | read       |
| ITV/tf-aws-rds             | read       |
| ITV/tf-aws-vpc-vpn         | read       |
| ITV/tf-aws-ebs             | read       |
| ITV/tf-aws-elasticache     | read       |
| ITV/tf-aws-route53-records | read       |
| ITV/tf-aws-elb             | read       |
| ITV/tf-aws-core-peering    | read       |
| ITV/tf-aws-efs             | read       |
| ITV/tf-aws-kms             | read       |
| ITV/tf-aws-core-ami        | read       |
| ITV/tf-aws-s3bucket        | read       |
| ITV/tf-aws-role-core       | read       |
| ITV/tf-aws-bastion-nodes   | read       |
| ITV/tf-aws-cloudtrail      | read       |
| ITV/tf-aws-lambda          | read       |
| ITV/tf-aws-asg-lb          | read       |
| ITV/tf-aws-cloudfront      | read       |
| ITV/tf-aws-waf-ip-rule     | read       |
| ITV/tf-aws-lb              | read       |
| ITV/tf-aws-role-logs       | read       |
| ITV/tf-aws-role-metrics    | read       |
| ITV/tf-aws-role-ci         | read       |
| ITV/tf-aws-role-ci-slave   | read       |
| ITV/tf-aws-asg-alb         | read       |
| ITV/tf-aws-asg-nlb         | read       |
| ITV/tf-aws-alb             | read       |
| ITV/tf-aws-nlb             | read       |
| ITV/tf-aws-role-bastion    | read       |
| ITV/tf-aws-product         | read       |
| ITV/tf-aws-ecosystem       | read       |
| ITV/tf-aws-environment     | read       |
| ITV/tf-aws-role-mirror     | read       |
+----------------------------+------------+

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment