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  }