{-# LANGUAGE OverloadedStrings #-} module Main where import Options.Applicative import qualified Data.Text as T import Network.Octohat (keysOfTeamInOrganization) import Network.Octohat.Types (runGitHub, OrganizationName(..), TeamName(..)) import System.OpensshGithubKeys (formatKey) import System.Timeout (timeout) import qualified Configuration.Dotenv as Dotenv data Options = Options { localUser :: String , organization :: String , team :: String , users :: [String] , dotfile :: Maybe FilePath } deriving (Show) fetchKeys :: Options -> IO () fetchKeys opts = do if any (\u -> localUser opts == u) (users opts) then do res <- runGitHub $ keysOfTeamInOrganization (OrganizationName $ T.pack $ organization opts) (TeamName $ T.pack $ team opts) case res of Right membersWithKeys -> putStrLn $ unlines $ concatMap formatKey membersWithKeys Left e -> error $ "Error retrieving keys for organization '" ++ show e else return () config :: Parser Options config = Options <$> argument str (metavar "AUTHENTICATE" <> help "Local user trying to authenticate currently") <*> strOption ( long "organization" <> short 'o' <> metavar "ORGANIZATION" <> help "GitHub organization from which to select teams") <*> strOption ( long "team" <> short 't' <> metavar "TEAM" <> help "GitHub team from which to select members' keys" ) <*> some (strOption ( long "user" <> short 'u' <> metavar "USER" <> help "A local user that we should try to authenticate using Github" )) <*> strOptional ( long "dotfile" <> short 'f' <> metavar "DOTFILE" <> help "File in 'dotfile' format specifying GITHUB_TOKEN" ) strOptional :: Mod OptionFields String -> Parser (Maybe String) strOptional flags = Just <$> strOption flags <|> pure Nothing main :: IO () main = do options <- execParser opts readDotenvFile options res <- timeout allowedTime (fetchKeys options) case res of Nothing -> error $ "Allowed time of " ++ show allowedTime ++ " microseconds exceeded while fetching keys from GitHub." Just _ -> return () where allowedTime = 5 * 1000000 -- Allowed time in microseconds for fetching keys opts = info (helper <*> config) ( fullDesc <> progDesc (unlines [ "Fetches ssh public keys from TEAMS under ORGANIZATION on GitHub." , "The output format is suitable for AuthorizedKeysCommand in" , "sshd_config. Requires a github token, which can be specified" , "in an environment variable GITHUB_TOKEN or in a dotenv file which" , "can be specified with the -f option."]) <> header "openssh-github-keys - fetches team member keys from GitHub" ) -- | Conditionally reads the ~/.github_token file if it exists. readDotenvFile :: Options -> IO () readDotenvFile opts = case dotfile opts of Just f -> Dotenv.loadFile False f Nothing -> return ()