github.com/esnet/gdg@v0.6.1-0.20240412190737-6b6eba9c14d8/cli/tools/auth_service_accounts.go (about) 1 package tools 2 3 import ( 4 "context" 5 "errors" 6 "github.com/bep/simplecobra" 7 "github.com/esnet/gdg/cli/support" 8 "github.com/esnet/gdg/internal/config" 9 "github.com/jedib0t/go-pretty/v6/table" 10 "log" 11 "log/slog" 12 13 "github.com/spf13/cobra" 14 "slices" 15 "sort" 16 "strconv" 17 ) 18 19 func newServiceAccountCmd() simplecobra.Commander { 20 description := "Manage api service-account" 21 return &support.SimpleCommand{ 22 NameP: "service-accounts", 23 Short: description, 24 Long: description, 25 CommandsList: []simplecobra.Commander{ 26 newListServiceAccountCmd(), 27 newDeleteServiceAccountCmd(), 28 newDeleteServiceAccountTokensCmd(), 29 newServiceAccount(), 30 newServiceAccountTokenCmd(), 31 }, 32 RunFunc: func(ctx context.Context, cd *simplecobra.Commandeer, rootCmd *support.RootCommand, args []string) error { 33 return cd.CobraCommand.Help() 34 }, 35 WithCFunc: func(cmd *cobra.Command, r *support.RootCommand) { 36 cmd.Aliases = []string{"service-account", "svcAcct", "svcAccts", "svc"} 37 }, 38 } 39 } 40 41 func newListServiceAccountCmd() simplecobra.Commander { 42 description := "List Service Accounts" 43 return &support.SimpleCommand{ 44 NameP: "list", 45 Short: description, 46 Long: description, 47 CommandsList: []simplecobra.Commander{}, 48 RunFunc: func(ctx context.Context, cd *simplecobra.Commandeer, rootCmd *support.RootCommand, args []string) error { 49 rootCmd.TableObj.AppendHeader(table.Row{"id", "service name", "role", "tokens", "token id", "token name", "expiration"}) 50 apiKeys := rootCmd.GrafanaSvc().ListServiceAccounts() 51 sort.SliceStable(apiKeys, func(i, j int) bool { 52 return apiKeys[i].ServiceAccount.ID < apiKeys[j].ServiceAccount.ID 53 }) 54 if len(apiKeys) == 0 { 55 slog.Info("No Service Accounts found") 56 } else { 57 for _, apiKey := range apiKeys { 58 59 rootCmd.TableObj.AppendRow(table.Row{apiKey.ServiceAccount.ID, apiKey.ServiceAccount.Name, apiKey.ServiceAccount.Role, apiKey.ServiceAccount.Tokens}) 60 if apiKey.Tokens != nil { 61 sort.SliceStable(apiKey.Tokens, func(i, j int) bool { 62 return apiKey.Tokens[i].ID < apiKey.Tokens[j].ID 63 }) 64 for _, token := range apiKey.Tokens { 65 var formattedDate string = token.Expiration.String() 66 date, _ := token.Expiration.Value() 67 if date.(string) == "0001-01-01T00:00:00.000Z" { 68 formattedDate = "No Expiration" 69 } 70 rootCmd.TableObj.AppendRow(table.Row{"", "", "", "", token.ID, token.Name, formattedDate}) 71 } 72 } 73 } 74 rootCmd.Render(cd.CobraCommand, apiKeys) 75 } 76 77 return nil 78 }, 79 } 80 } 81 82 func newDeleteServiceAccountTokensCmd() simplecobra.Commander { 83 description := "delete all tokens for Service Account from grafana" 84 return &support.SimpleCommand{ 85 NameP: "clearTokens", 86 Short: description, 87 Long: description, 88 CommandsList: []simplecobra.Commander{}, 89 RunFunc: func(ctx context.Context, cd *simplecobra.Commandeer, rootCmd *support.RootCommand, args []string) error { 90 if len(args) < 1 { 91 return errors.New("requires a service account ID to be specified") 92 } 93 idStr := args[0] 94 id, err := strconv.ParseInt(idStr, 10, 64) 95 if err != nil { 96 log.Fatalf("unable to parse %s as a valid numeric value", idStr) 97 } 98 99 slog.Info("Deleting Service Accounts Tokens for context", 100 "serviceAccountId", id, 101 "context", config.Config().GetGDGConfig().GetContext()) 102 savedFiles := rootCmd.GrafanaSvc().DeleteServiceAccountTokens(id) 103 rootCmd.TableObj.AppendHeader(table.Row{"serviceID", "type", "token_name"}) 104 if len(savedFiles) == 0 { 105 slog.Info("No Service Accounts tokens found") 106 } else { 107 for _, token := range savedFiles { 108 rootCmd.TableObj.AppendRow(table.Row{id, "service token", token}) 109 } 110 rootCmd.Render(cd.CobraCommand, savedFiles) 111 } 112 return nil 113 }, 114 } 115 } 116 117 func newDeleteServiceAccountCmd() simplecobra.Commander { 118 description := "delete all Service Accounts from grafana" 119 return &support.SimpleCommand{ 120 NameP: "clear", 121 Short: description, 122 Long: description, 123 CommandsList: []simplecobra.Commander{}, 124 RunFunc: func(ctx context.Context, cd *simplecobra.Commandeer, rootCmd *support.RootCommand, args []string) error { 125 savedFiles := rootCmd.GrafanaSvc().DeleteAllServiceAccounts() 126 slog.Info("Delete Service Accounts for context", "context", config.Config().GetGDGConfig().GetContext()) 127 rootCmd.TableObj.AppendHeader(table.Row{"type", "filename"}) 128 if len(savedFiles) == 0 { 129 slog.Info("No Service Accounts found") 130 } else { 131 for _, file := range savedFiles { 132 rootCmd.TableObj.AppendRow(table.Row{"user", file}) 133 } 134 rootCmd.Render(cd.CobraCommand, savedFiles) 135 } 136 return nil 137 }, 138 } 139 } 140 141 func newServiceAccount() simplecobra.Commander { 142 description := "newService <serviceName> <role> [ttl in seconds]" 143 return &support.SimpleCommand{ 144 NameP: "newService", 145 Short: description, 146 Long: description, 147 CommandsList: []simplecobra.Commander{}, 148 RunFunc: func(ctx context.Context, cd *simplecobra.Commandeer, rootCmd *support.RootCommand, args []string) error { 149 if len(args) < 2 { 150 return errors.New("requires a key name and a role('admin','viewer','editor') [ttl optional] ") 151 } 152 name := args[0] 153 role := args[1] 154 ttl := "0" 155 if len(args) > 2 { 156 ttl = args[2] 157 } 158 var ( 159 expiration int64 160 err error 161 ) 162 163 expiration, err = strconv.ParseInt(ttl, 10, 64) 164 if err != nil { 165 expiration = 0 166 } 167 168 if !slices.Contains([]string{"admin", "editor", "viewer"}, role) { 169 log.Fatal("Invalid role specified") 170 } 171 serviceAcct, err := rootCmd.GrafanaSvc().CreateServiceAccount(name, role, expiration) 172 if err != nil { 173 log.Fatal("unable to create api key", "error", err) 174 } else { 175 176 rootCmd.TableObj.AppendHeader(table.Row{"id", "name", "role"}) 177 rootCmd.TableObj.AppendRow(table.Row{serviceAcct.ID, serviceAcct.Name, serviceAcct.Role}) 178 rootCmd.Render(cd.CobraCommand, 179 map[string]interface{}{"id": serviceAcct.ID, "name": serviceAcct.Name, "role": serviceAcct.Role}) 180 } 181 return nil 182 }, 183 } 184 } 185 186 func newServiceAccountTokenCmd() simplecobra.Commander { 187 description := "newToken <serviceAccountID> <name> [ttl in seconds]" 188 return &support.SimpleCommand{ 189 NameP: "newToken", 190 Short: description, 191 Long: description, 192 CommandsList: []simplecobra.Commander{newTokensCmd()}, 193 RunFunc: func(ctx context.Context, cd *simplecobra.Commandeer, rootCmd *support.RootCommand, args []string) error { 194 if len(args) < 2 { 195 return errors.New("requires a service-account ID and token name [ttl optional] ") 196 } 197 serviceIDRaw := args[0] 198 name := args[1] 199 ttl := "0" 200 if len(args) > 2 { 201 ttl = args[2] 202 } 203 var ( 204 expiration int64 205 err error 206 ) 207 208 serviceID, err := strconv.ParseInt(serviceIDRaw, 10, 64) 209 if err != nil { 210 log.Fatal("unable to parse serviceID, make sure it's a numeric value") 211 } 212 expiration, err = strconv.ParseInt(ttl, 10, 64) 213 if err != nil { 214 expiration = 0 215 } 216 217 key, err := rootCmd.GrafanaSvc().CreateServiceAccountToken(serviceID, name, expiration) 218 if err != nil { 219 log.Fatal("unable to create api key", "err", err) 220 } else { 221 222 rootCmd.TableObj.AppendHeader(table.Row{"serviceID", "token_id", "name", "token"}) 223 rootCmd.TableObj.AppendRow(table.Row{serviceID, key.ID, key.Name, key.Key}) 224 rootCmd.Render(cd.CobraCommand, 225 map[string]interface{}{"serviceID": serviceID, 226 "token_id": key.ID, 227 "name": key.Name, 228 "token": key.Key}) 229 } 230 231 return nil 232 }, 233 } 234 }