Skip to content

Commit c3f12cb

Browse files
authored
Better permission reporting and CSV file support for groovy examples (#270)
* Added CSV and permission details to PrintRepoAccess * bumped library number * introduced -c parameter to support reading repository names from CSV files * introduced -p parameter to print detailed permissions about user access * Added CSV file support for AuditUsers * introduced -c option to read users from CSV file * renamed skipPublicRepo option and made it opt-in * use CSV file header for generated output * Added extended permission reporting option * added -e switch to split repositories based on access type
1 parent c7ed3f6 commit c3f12cb

File tree

2 files changed

+97
-21
lines changed

2 files changed

+97
-21
lines changed

api/groovy/AuditUsers.groovy

Lines changed: 70 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,38 +2,40 @@
22

33
/**
44
* groovy script to show all repositories that can be accessed by given users on an GitHub Enterprise instance
5-
*
6-
*
5+
*
6+
*
77
* Run 'groovy AuditUsers.groovy' to see the list of command line options
8-
*
8+
*
99
* First run may take some time as required dependencies have to get downloaded, then it should be quite fast
10-
*
10+
*
1111
* If you do not have groovy yet, run 'brew install groovy'
1212
*/
1313

14-
@Grab(group='org.kohsuke', module='github-api', version='1.75')
14+
@Grab(group='org.kohsuke', module='github-api', version='1.99')
1515
@Grab(group='org.codehaus.groovy.modules.http-builder', module='http-builder', version='0.7.2' )
1616
import org.kohsuke.github.GitHub
1717
import groovyx.net.http.RESTClient
1818
import static groovyx.net.http.ContentType.*
1919
import groovy.json.JsonOutput
20+
import org.kohsuke.github.GHMyself.RepositoryListFilter
2021

2122

2223
// parsing command line args
2324
cli = new CliBuilder(usage: 'groovy AuditUsers.groovy [options] [user accounts]\nReports all repositories that can be accessed by given users')
2425
cli.t(longOpt: 'token', 'personal access token of a GitHub Enterprise site admin with repo skope (or use GITHUB_TOKEN env variable)', required: false , args: 1 )
2526
cli.u(longOpt: 'url', 'GitHub Enterprise URL (or use GITHUB_URL env variable), e.g. https://myghe.com', required: false , args: 1 )
26-
cli.s(longOpt: 'skipPublicRepos', 'Do not print publicly available repositories at the end of the report', required: false , args: 0 )
27+
cli.p(longOpt: 'printPublicRepos', 'Print publicly available repositories at the end of the report', required: false , args: 0 )
2728
cli.h(longOpt: 'help', 'Print this usage info', required: false , args: 0 )
29+
cli.c(longOpt: 'csv', 'CSV file with users in the format produced by stafftools/reports (show access for all contained users)', required: false, args: 1)
30+
cli.e(longOpt: 'extendedpermissions', 'Print extended permissions (ALL, OWNER, PUBLIC, PRIVATE, MEMBER) why a repository can be accessed by that user, needs 4 times more API calls', required: false, args: 0)
2831

2932
OptionAccessor opt = cli.parse(args)
3033

3134
token = opt.t?opt.t:System.getenv("GITHUB_TOKEN")
3235
url = opt.u?opt.u:System.getenv("GITHUB_URL")
33-
listOnly = opt.l
3436

3537
// bail out if help parameter was supplied or not sufficient input to proceed
36-
if (opt.h || !token || !url || opt.arguments().size() == 0) {
38+
if (opt.h || !token || !url) {
3739
cli.usage()
3840
return
3941
}
@@ -44,10 +46,37 @@ url = url.replaceAll('/\$', "")
4446

4547
RESTClient restSiteAdmin = getGithubApi(url , token)
4648

49+
// printing header
50+
51+
println "user,accesstype,repo,owner,private,read,write,admin,url"
52+
4753
// iterate over all supplied users
4854
opt.arguments().each {
49-
user=it
50-
println "Showing repositories accessible for user ${user} ... "
55+
printAccessRightsForUser(it, restSiteAdmin, opt.e)
56+
}
57+
58+
if (opt.c) {
59+
userCSVFile = new File(opt.c)
60+
if (!userCSVFile.isFile()) {
61+
printErr "${userCSVFile.canonicalPath} is not a file"
62+
return
63+
}
64+
boolean firstLine=true
65+
userCSVFile.splitEachLine(',') { line ->
66+
if (firstLine) {
67+
firstLine=false
68+
} else {
69+
// only display access rights for non-suspended users
70+
if (line[5] == "false")
71+
printAccessRightsForUser(line[2], restSiteAdmin, opt.e)
72+
}
73+
}
74+
}
75+
76+
// END MAIN
77+
78+
def printAccessRightsForUser(user, restSiteAdmin, extendedPermissions) {
79+
//println "Showing repositories accessible for user ${user} ... "
5180
try {
5281
// get temporary access token for given user
5382
resp = restSiteAdmin.post(
@@ -57,13 +86,20 @@ opt.arguments().each {
5786

5887
assert resp.data.token != null
5988
userToken = resp.data.token
60-
89+
6190
try {
62-
// list all accessible repositories in organizations and personal repositories of this user
63-
userRepos = GitHub.connectToEnterprise("${url}/api/v3", userToken).getMyself().listAllRepositories()
91+
gitHubUser = GitHub.connectToEnterprise("${url}/api/v3", userToken).getMyself()
92+
93+
Set repositories = []
6494

65-
// further fields available on http://github-api.kohsuke.org/apidocs/org/kohsuke/github/GHRepository.html#method_summary
66-
userRepos.each { println "user: ${user}, repo: ${it.name}, owner: ${it.ownerName}, private: ${it.private}, read: ${it.hasPullAccess()}, write: ${it.hasPushAccess()}, admin: ${it.hasAdminAccess()}, url: ${it.getHtmlUrl()}" }
95+
if (!extendedPermissions) {
96+
printRepoAccess(gitHubUser, RepositoryListFilter.ALL, repositories)
97+
} else {
98+
printRepoAccess(gitHubUser, RepositoryListFilter.OWNER, repositories)
99+
printRepoAccess(gitHubUser, RepositoryListFilter.MEMBER, repositories)
100+
printRepoAccess(gitHubUser, RepositoryListFilter.PRIVATE, repositories)
101+
printRepoAccess(gitHubUser, RepositoryListFilter.PUBLIC, repositories)
102+
}
67103
}
68104
finally {
69105
// delete the personal access token again even if we ran into an exception
@@ -73,11 +109,11 @@ opt.arguments().each {
73109
println ""
74110
} catch (Exception e) {
75111
e.printStackTrace()
76-
println "An error occurred while fetching repositories for user ${user}, continuing with the next user ..."
112+
printErr "An error occurred while fetching repositories for user ${user}, continuing with the next user ..."
77113
}
78114
}
79115

80-
if (!opt.s) {
116+
if (opt.p) {
81117
println "Showing repositories accessible by any logged in user ..."
82118
publicRepos = GitHub.connectToEnterprise("${url}/api/v3", token).listAllPublicRepositories()
83119
// further fields on http://github-api.kohsuke.org/apidocs/org/kohsuke/github/GHRepository.html#method_summary
@@ -91,3 +127,20 @@ def RESTClient getGithubApi(url, token) {
91127
it
92128
}
93129
}
130+
131+
def printRepoAccess(gitHubUser, repoTypeFilter, alreadyProcessedRepos) {
132+
// list all accessible repositories in organizations and personal repositories of this user
133+
userRepos = gitHubUser.listRepositories(100, repoTypeFilter)
134+
135+
// further fields available on http://github-api.kohsuke.org/apidocs/org/kohsuke/github/GHRepository.html#method_summary
136+
userRepos.each {
137+
if (!alreadyProcessedRepos.contains(it.htmlUrl)) {
138+
println "${gitHubUser.login},${repoTypeFilter},${it.name},${it.ownerName},${it.private},${it.hasPullAccess()},${it.hasPushAccess()},${it.hasAdminAccess()},${it.htmlUrl}"
139+
alreadyProcessedRepos.add(it.htmlUrl)
140+
}
141+
}
142+
}
143+
144+
def printErr (msg) {
145+
System.err.println "ERROR: ${msg}"
146+
}

api/PrintRepoAccess.groovy renamed to api/groovy/PrintRepoAccess.groovy

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* groovy script to show all users that can access a given repository in a GitHub Enterprise instance
55
*
66
* Run 'groovy PrintRepoAccess.groovy' to see the list of command line options
7-
*
7+
*
88
* Example on how to list access rights for repos foo/bar and bar/foo on GitHub Enterprise instance https://foobar.com:
99
*
1010
* groovy PrintRepoAccess.groovy -u https://foobar.com -t <access token> foo/bar bar/foo
@@ -20,26 +20,29 @@
2020
*
2121
* Apart from Groovy (and Java), you do not need to install any libraries on your system as the script will download them when you first start it
2222
* The first run may take some time as required dependencies have to get downloaded, then it should be quite fast
23-
*
23+
*
2424
* If you do not have groovy yet, run 'brew install groovy' on a Mac, for Windows and Linux follow the instructions here:
2525
* http://groovy-lang.org/install.html
2626
*
2727
*/
2828

29-
@Grab(group='org.kohsuke', module='github-api', version='1.75')
29+
@Grab(group='org.kohsuke', module='github-api', version='1.99')
3030
import org.kohsuke.github.GitHub
3131

3232
// parsing command line args
3333
cli = new CliBuilder(usage: 'groovy PrintRepoAccess.groovy [options] [repos]\nPrint out users that can access the repos specified, ALL if public repo')
3434
cli.t(longOpt: 'token', 'personal access token of a GitHub Enterprise site admin with repo scope (or use GITHUB_TOKEN env variable)', required: false , args: 1 )
3535
cli.u(longOpt: 'url', 'GitHub Enterprise URL (or use GITHUB_URL env variable), e.g. https://myghe.com', required: false , args: 1 )
3636
cli.l(longOpt: 'localDirectory', 'Directory with org/repo directory structure (show access for all contained repos)', required: false, args: 1)
37+
cli.c(longOpt: 'csv', 'CSV file with repositories in the format produced by stafftools/reports (show access for all contained repos)', required: false, args: 1)
3738
cli.h(longOpt: 'help', 'Print this usage info', required: false , args: 0 )
39+
cli.p(longOpt: 'permissions', 'Print user permissions on repo', required: false , args: 0 )
3840

3941
OptionAccessor opt = cli.parse(args)
4042

4143
token = opt.t?opt.t:System.getenv("GITHUB_TOKEN")
4244
url = opt.u?opt.u:System.getenv("GITHUB_URL")
45+
printPerms = opt.p
4346

4447
// bail out if help parameter was supplied or not sufficient input to proceed
4548
if (opt.h || !token || !url ) {
@@ -68,6 +71,15 @@ if (opt.l) {
6871
printAccessRightsForStoredRepos(localRepoStore)
6972
}
7073

74+
if (opt.c) {
75+
repoCSVFile = new File(opt.c)
76+
if (!repoCSVFile.isFile()) {
77+
printErr "${repoCSVFile.canonicalPath} is not a file"
78+
return
79+
}
80+
printAccessRightsForCSVFile(repoCSVFile)
81+
}
82+
7183
// END OF MAIN
7284

7385
def printAccessRightsForRepo(org, repo) {
@@ -82,7 +94,7 @@ def printAccessRightsForRepo(org, repo) {
8294
println "${org}/${repo},ALL"
8395
} else {
8496
ghRepo.getCollaboratorNames().each {
85-
println "${org}/${repo},${it}"
97+
println "${org}/${repo},${it}"+ (printPerms?","+ghRepo.getPermission(it):"")
8698
}
8799
}
88100
} catch (Exception e) {
@@ -111,6 +123,17 @@ def printAccessRightsForStoredRepos(localRepoStore) {
111123
}
112124
}
113125

126+
def printAccessRightsForCSVFile(csvFile) {
127+
boolean firstLine=true
128+
repoCSVFile.splitEachLine(',') { line ->
129+
if (firstLine) {
130+
firstLine=false
131+
} else {
132+
printAccessRightsForRepo(line[3],line[5])
133+
}
134+
}
135+
}
136+
114137
def printErr (msg) {
115138
System.err.println "ERROR: ${msg}"
116139
}

0 commit comments

Comments
 (0)