Merge key fetcher #6
|
@ -0,0 +1,14 @@
|
||||||
|
Use the Github API to fetch configured SSH keys for a team.
|
||||||
|
|
||||||
|
Build by (making sure your GOPATH environment variable is properly configured) -
|
||||||
|
```
|
||||||
|
go get
|
||||||
|
go build
|
||||||
|
```
|
||||||
|
|
||||||
|
Run
|
||||||
|
```
|
||||||
|
./fetch-keys --help
|
||||||
|
```
|
||||||
|
|
||||||
|
The tool uses an OAuth Token to connect to the Github API, make sure you've added a personal access token via your [Github account settings](https://github.com/settings/tokens) and have either properly edited the [authtoken.go](authtoken.go#L3) source file (!!! with caution), or pass the appropriate token via the command line.
|
|
@ -0,0 +1,3 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
const AuthToken = "c90e06359175cfccc4db21f60c44b83d93ccbd28"
|
|
@ -0,0 +1,243 @@
|
||||||
|
/*-
|
||||||
|
* ----------------------------------------------------------------------------
|
||||||
|
* "THE BEER-WARE LICENSE" (Revision 69):
|
||||||
|
* <syrinx@FreeBSD.org> wrote this file. As long as you retain this notice you
|
||||||
|
* can do whatever you want with this stuff. If we meet some day, and you think
|
||||||
|
* this stuff is worth it, you can buy me a beer in return. -Shteryana Shopova
|
||||||
|
* ----------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"github.com/akamensky/argparse"
|
||||||
|
"github.com/google/go-github/github" // with go modules disabled
|
||||||
|
"golang.org/x/oauth2"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
parser := argparse.NewParser("sshkeys", "Fetch SSH keys for a Github team members")
|
||||||
|
authToken := parser.String("a", "authtoken", &argparse.Options{Required: false, Help: "Github Auth token", Default: AuthToken})
|
||||||
|
keysDir := parser.String("d", "directory", &argparse.Options{Required: false, Help: "Path where to store the key files", Default: "./"})
|
||||||
|
ghOrganization := parser.String("o", "org", &argparse.Options{Required: false, Help: "Github Organization name", Default: "OpenFest"})
|
||||||
|
fetchPgp := parser.Flag("p", "pgp-keys", &argparse.Options{Required: false, Help: "Fetch configured PGP key ids", Default: false})
|
||||||
|
quiet := parser.Flag("q", "quiet", &argparse.Options{Required: false, Help: "Skip output to stdout", Default: false})
|
||||||
|
ghTeam := parser.String("t", "team", &argparse.Options{Required: false, Help: "Github Team name, 'all' for all members of the organization", Default: "NOC"})
|
||||||
|
verbose := parser.Flag("v", "verbose", &argparse.Options{Required: false, Help: "Verbose output: print keys to stdout", Default: false})
|
||||||
|
|
||||||
|
// Parse input
|
||||||
|
err := parser.Parse(os.Args)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Print(parser.Usage(err))
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if *verbose == false {
|
||||||
|
fi, err := os.Lstat(*keysDir)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(*keysDir, ": target directory error :", err)
|
||||||
|
os.Exit(1)
|
||||||
|
} else {
|
||||||
|
if fi.Mode().IsDir() == false {
|
||||||
|
fmt.Println(*keysDir, ": target directory error : not a directory - ", fi.Mode())
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
ts := oauth2.StaticTokenSource(
|
||||||
|
&oauth2.Token{AccessToken: *authToken},
|
||||||
|
)
|
||||||
|
tc := oauth2.NewClient(ctx, ts)
|
||||||
|
|
||||||
|
client := github.NewClient(tc)
|
||||||
|
|
||||||
|
teamMembers := fetchUsers(client, ghOrganization, ghTeam)
|
||||||
|
for _, user := range teamMembers {
|
||||||
|
if *quiet == false {
|
||||||
|
fmt.Println("Fetching keys for", *user)
|
||||||
|
}
|
||||||
|
var sshKeys bytes.Buffer
|
||||||
|
var pgpKeys bytes.Buffer
|
||||||
|
|
||||||
|
for nextPage := 0; ; {
|
||||||
|
|
||||||
|
// list all teams an org for the current user
|
||||||
|
opt := &github.ListOptions{nextPage, 50}
|
||||||
|
keys, rsp, err := client.Users.ListKeys(ctx, *user, opt)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("client.Users.ListKeys error: ", err)
|
||||||
|
os.Exit(-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if rsp == nil {
|
||||||
|
fmt.Println("Users.ListKeys returned empty response: ", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, key := range keys {
|
||||||
|
if *verbose == true {
|
||||||
|
fmt.Println(*key.Key)
|
||||||
|
}
|
||||||
|
sshKeys.WriteString(*key.Key)
|
||||||
|
sshKeys.WriteString("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
if rsp.NextPage == 0 || nextPage == rsp.NextPage {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
nextPage = rsp.NextPage
|
||||||
|
}
|
||||||
|
|
||||||
|
if *quiet == false && *verbose == false {
|
||||||
|
fmt.Println("Writing to", *keysDir+"/"+*user+".key")
|
||||||
|
}
|
||||||
|
err := ioutil.WriteFile(*keysDir+"/"+*user+".key", sshKeys.Bytes(), 0444)
|
||||||
|
if err != nil && *verbose == false {
|
||||||
|
fmt.Println(*user+".key error ", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if *fetchPgp == true {
|
||||||
|
for nextPage := 0; ; {
|
||||||
|
|
||||||
|
// list all teams an org for the current user
|
||||||
|
opt := &github.ListOptions{nextPage, 50}
|
||||||
|
keys, rsp, err := client.Users.ListGPGKeys(ctx, *user, opt)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("client.Users.ListGPGKeys error: ", err)
|
||||||
|
os.Exit(-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if rsp == nil {
|
||||||
|
fmt.Println("Users.ListGPGKeys returned empty response: ", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, key := range keys {
|
||||||
|
if *verbose == true {
|
||||||
|
fmt.Println(*key.KeyID)
|
||||||
|
}
|
||||||
|
pgpKeys.WriteString(*key.KeyID)
|
||||||
|
pgpKeys.WriteString("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
if rsp.NextPage == 0 || nextPage == rsp.NextPage {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
nextPage = rsp.NextPage
|
||||||
|
}
|
||||||
|
if *quiet == false && *verbose == false {
|
||||||
|
fmt.Println("Writing to", *keysDir+"/"+*user+".gpg")
|
||||||
|
}
|
||||||
|
err = ioutil.WriteFile(*keysDir+"/"+*user+".gpg", pgpKeys.Bytes(), 0444)
|
||||||
|
if err != nil && *verbose == false {
|
||||||
|
fmt.Println(*user+".gpg error ", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func fetchUsers(client *github.Client, org *string, team *string) (teamMembers []*string) {
|
||||||
|
var targetTeam *github.Team
|
||||||
|
|
||||||
|
if team == nil || *team == "all" {
|
||||||
|
for nextPage := 0; ; {
|
||||||
|
// list all members for the given organization's team
|
||||||
|
opt := &github.ListMembersOptions{
|
||||||
|
PublicOnly: false,
|
||||||
|
ListOptions: github.ListOptions{nextPage, 50},
|
||||||
|
}
|
||||||
|
|
||||||
|
users, rsp, err := client.Organizations.ListMembers(context.Background(), *org, opt)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("client.Organizations.ListMembers ", err)
|
||||||
|
os.Exit(-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if rsp == nil {
|
||||||
|
fmt.Println("client.Organizations.ListMembers: ", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, user := range users {
|
||||||
|
teamMembers = append(teamMembers, user.Login)
|
||||||
|
}
|
||||||
|
|
||||||
|
if rsp.NextPage == 0 || nextPage == rsp.NextPage {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
nextPage = rsp.NextPage
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for nextPage := 0; ; {
|
||||||
|
// list all teams for the specified org
|
||||||
|
opt := &github.ListOptions{nextPage, 50}
|
||||||
|
teams, rsp, err := client.Teams.ListTeams(context.Background(), *org, opt)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("client.ListTeams error: ", err)
|
||||||
|
os.Exit(-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if rsp == nil {
|
||||||
|
fmt.Println("client.ListTeams returned empty response: ", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ghTeam := range teams {
|
||||||
|
if *ghTeam.Name == *team {
|
||||||
|
targetTeam = ghTeam
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if rsp.NextPage == 0 || nextPage == rsp.NextPage {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
nextPage = rsp.NextPage
|
||||||
|
}
|
||||||
|
|
||||||
|
if targetTeam == nil {
|
||||||
|
fmt.Println(*team, " team not found in ", *org)
|
||||||
|
os.Exit(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
for nextPage := 0; ; {
|
||||||
|
// list all members for the given organization's team
|
||||||
|
opt := &github.TeamListTeamMembersOptions{
|
||||||
|
Role: "all",
|
||||||
|
ListOptions: github.ListOptions{nextPage, 50},
|
||||||
|
}
|
||||||
|
|
||||||
|
users, rsp, err := client.Teams.ListTeamMembers(context.Background(), *targetTeam.ID, opt)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("client.Teams.ListTeamMembers ", err)
|
||||||
|
os.Exit(-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if rsp == nil {
|
||||||
|
fmt.Println("client.Teams.ListTeamMembers: ", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, user := range users {
|
||||||
|
teamMembers = append(teamMembers, user.Login)
|
||||||
|
}
|
||||||
|
|
||||||
|
if rsp.NextPage == 0 || nextPage == rsp.NextPage {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
nextPage = rsp.NextPage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return teamMembers
|
||||||
|
}
|
Loading…
Reference in New Issue