├── .gitignore ├── README.md └── deez_factors.go /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | whitelist.txt 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Deez Factors 2 | Looks for people in the GitHub organization that do not have 2-factor 3 | authentication turned on. Now you can tell your users to `Get Deez Factors!` 4 | 5 | ## How to set it up 6 | You need to set an environment variable, `GITHUB_API_KEY`, either in your 7 | shell or by adding it to a file called `.env` in the format `GITHUB_API_KEY:blahblah`. 8 | The token I used was a GitHub Personal Access Token. Then you just run the program with the org name. 9 | 10 | `deez_factors microsoft` 11 | -------------------------------------------------------------------------------- /deez_factors.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | "bufio" 8 | "strings" 9 | "flag" 10 | "github.com/joho/godotenv" 11 | "github.com/google/go-github/github" 12 | "golang.org/x/oauth2" 13 | ) 14 | 15 | func readWhitelist(path string) ([]string, error) { 16 | var lines []string 17 | file, err := os.Open(path) 18 | 19 | // There might be a problem opening the file. If so, 20 | // return the error 21 | if err != nil { 22 | return lines, err 23 | } 24 | 25 | // No error, so make sure we close the file when we're done 26 | defer file.Close() 27 | 28 | // Now read it into an array 29 | scanner := bufio.NewScanner(file) 30 | for scanner.Scan() { 31 | if strings.HasPrefix(scanner.Text(), "#") { 32 | // skip lines that start with # 33 | continue 34 | } 35 | lines = append(lines, scanner.Text()) 36 | } 37 | return lines, nil 38 | } 39 | 40 | func checkWhiteList(name string, whitelist []string) (bool) { 41 | for _, value := range whitelist { 42 | if name == value { 43 | return true 44 | } 45 | } 46 | return false 47 | } 48 | 49 | 50 | func main() { 51 | // load environment variables from .env and org from command line 52 | 53 | flag.Parse() 54 | if len(flag.Args()) == 0 { 55 | fmt.Println("Why you no specify org name? Usage is \"deez_factors org\"") 56 | os.Exit(1) 57 | } 58 | 59 | org_name := flag.Arg(0) 60 | 61 | err := godotenv.Load() 62 | if err != nil { 63 | log.Fatal("Error loading .env file") 64 | } 65 | 66 | // read the whitelist of user names that are allowed to have 67 | // 2FA turned off 68 | whitelist, err := readWhitelist("whitelist.txt") 69 | if err != nil { 70 | log.Println("Error reading whitelist: ", err, "-- proceeding with empty whitelist") 71 | } 72 | 73 | //authenticate to github 74 | ts := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: os.Getenv("GITHUB_API_KEY")}) 75 | tc := oauth2.NewClient(oauth2.NoContext, ts) 76 | 77 | // create a github client using the token from above 78 | client := github.NewClient(tc) 79 | 80 | // Get a list of org members that don't have 2FA enabled 81 | // Need to use a loop because there may be multiple pages 82 | // of users. 83 | var allUsers []github.User 84 | options := &github.ListMembersOptions{Filter: "2fa_disabled"} 85 | for { 86 | users, response, _ := client.Organizations.ListMembers(org_name, options) 87 | allUsers = append(allUsers, users...) 88 | if response.NextPage == 0 { 89 | break 90 | } 91 | options.ListOptions.Page = response.NextPage 92 | } 93 | 94 | // Loop over the list of users and print their name 95 | // User structs store values as pointers so we need to use 96 | // the * to get the value 97 | 98 | // Also need to use a different counter than the one that 99 | // comes with range because otherwise when we skip 100 | // whitelisted rows we end up with gaps in the numbers 101 | counter := 1 102 | for _, v := range allUsers { 103 | // If the user is whitelisted, then move on 104 | if checkWhiteList(*v.Login, whitelist) { 105 | continue 106 | } 107 | // Try to get more information about the user 108 | user, _, _ := client.Users.Get(*v.Login) 109 | 110 | fmt.Printf("%02d: ", counter) 111 | fmt.Print(*v.Login, " - ") 112 | 113 | if user.Name != nil { 114 | fmt.Print(*user.Name) 115 | } else { 116 | fmt.Print("No Public Name") 117 | } 118 | 119 | fmt.Print(" - ") 120 | if user.Email != nil { 121 | fmt.Print(*user.Email) 122 | } else { 123 | fmt.Print("No Public Email") 124 | } 125 | 126 | fmt.Print("\n") 127 | counter++ 128 | } 129 | 130 | } 131 | --------------------------------------------------------------------------------