feat #43: add CLI commands to manage backends
This commit is contained in:
parent
fadef50bc2
commit
74cb316d78
8 changed files with 357 additions and 1 deletions
40
polyculeconnect/cmd/backend.go
Normal file
40
polyculeconnect/cmd/backend.go
Normal file
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
Copyright © 2023 NAME HERE <EMAIL ADDRESS>
|
||||
|
||||
*/
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// backendCmd represents the backend command
|
||||
var backendCmd = &cobra.Command{
|
||||
Use: "backend",
|
||||
Short: "A brief description of your command",
|
||||
Long: `A longer description that spans multiple lines and likely contains examples
|
||||
and usage of using your command. For example:
|
||||
|
||||
Cobra is a CLI library for Go that empowers applications.
|
||||
This application is a tool to generate the needed files
|
||||
to quickly create a Cobra application.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Println("backend called")
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(backendCmd)
|
||||
|
||||
// Here you will define your flags and configuration settings.
|
||||
|
||||
// Cobra supports Persistent Flags which will work for this command
|
||||
// and all subcommands, e.g.:
|
||||
// backendCmd.PersistentFlags().String("foo", "", "A help for foo")
|
||||
|
||||
// Cobra supports local flags which will only run when this command
|
||||
// is called directly, e.g.:
|
||||
// backendCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
||||
}
|
50
polyculeconnect/cmd/backend_add.go
Normal file
50
polyculeconnect/cmd/backend_add.go
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
Copyright © 2023 NAME HERE <EMAIL ADDRESS>
|
||||
*/
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
backendID string
|
||||
backendName string
|
||||
backendIssuer string
|
||||
)
|
||||
|
||||
// backendAddCmd represents the add command
|
||||
var backendAddCmd = &cobra.Command{
|
||||
Use: "add",
|
||||
Short: "Add a new backend to the storage",
|
||||
Long: `Add a new backend to the storage.
|
||||
|
||||
Parameters to provide:
|
||||
- id: Unique ID to represent the backend in the storage
|
||||
- name: Human readable name to represent the backend. It will be used by
|
||||
the user in the authentication page to select a backend during
|
||||
authentication
|
||||
- issuer: Full hostname of the OIDC provider, e.g. 'https://github.com'`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Println("add called")
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
backendCmd.AddCommand(backendAddCmd)
|
||||
|
||||
// Here you will define your flags and configuration settings.
|
||||
|
||||
// Cobra supports Persistent Flags which will work for this command
|
||||
// and all subcommands, e.g.:
|
||||
// backendAddCmd.PersistentFlags().String("foo", "", "A help for foo")
|
||||
|
||||
// Cobra supports local flags which will only run when this command
|
||||
// is called directly, e.g.:
|
||||
// backendAddCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
||||
backendAddCmd.Flags().StringVarP(&backendID, "id", "i", "", "ID to identify the backend in the storage")
|
||||
backendAddCmd.Flags().StringVarP(&backendName, "name", "n", "", "Name to represent the backend")
|
||||
backendAddCmd.Flags().StringVarP(&backendIssuer, "issuer", "d", "", "Full hostname of the backend")
|
||||
}
|
39
polyculeconnect/cmd/backend_remove.go
Normal file
39
polyculeconnect/cmd/backend_remove.go
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
Copyright © 2023 NAME HERE <EMAIL ADDRESS>
|
||||
*/
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// backendRemoveCmd represents the remove command
|
||||
var backendRemoveCmd = &cobra.Command{
|
||||
Use: "remove",
|
||||
Short: "A brief description of your command",
|
||||
Long: `A longer description that spans multiple lines and likely contains examples
|
||||
and usage of using your command. For example:
|
||||
|
||||
Cobra is a CLI library for Go that empowers applications.
|
||||
This application is a tool to generate the needed files
|
||||
to quickly create a Cobra application.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Println("remove called")
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
backendCmd.AddCommand(backendRemoveCmd)
|
||||
|
||||
// Here you will define your flags and configuration settings.
|
||||
|
||||
// Cobra supports Persistent Flags which will work for this command
|
||||
// and all subcommands, e.g.:
|
||||
// backendRemoveCmd.PersistentFlags().String("foo", "", "A help for foo")
|
||||
|
||||
// Cobra supports local flags which will only run when this command
|
||||
// is called directly, e.g.:
|
||||
// backendRemoveCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
||||
}
|
97
polyculeconnect/cmd/backend_show.go
Normal file
97
polyculeconnect/cmd/backend_show.go
Normal file
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
Copyright © 2023 NAME HERE <EMAIL ADDRESS>
|
||||
*/
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"git.faercol.me/faercol/polyculeconnect/polyculeconnect/config"
|
||||
"git.faercol.me/faercol/polyculeconnect/polyculeconnect/services"
|
||||
"git.faercol.me/faercol/polyculeconnect/polyculeconnect/services/backend"
|
||||
"github.com/dexidp/dex/storage"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// backendShowCmd represents the show command for backends
|
||||
var backendShowCmd = &cobra.Command{
|
||||
Use: "show [backend_id]",
|
||||
Short: "Display installed backends",
|
||||
Long: `Display the configuration for the backends.
|
||||
|
||||
Pass the commands without arguments to display the list of currently installed backends
|
||||
Pass the optional 'id' argument to display the configuration for this specific backend`,
|
||||
Args: cobra.MaximumNArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
conf, err := config.New(configPath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
s, err := services.InitStorage(conf)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if len(args) > 0 {
|
||||
showBackend(args[0], backend.New(s))
|
||||
} else {
|
||||
listBackends(backend.New(s))
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func printProperty(key, value string) {
|
||||
fmt.Printf("\t- %s: %s\n", key, value)
|
||||
}
|
||||
|
||||
func showBackend(backendId string, backendService backend.Service) {
|
||||
backendConfig, err := backendService.GetBackend(backendId)
|
||||
if err != nil {
|
||||
if errors.Is(err, storage.ErrNotFound) {
|
||||
fmt.Fprintf(os.Stderr, "Backend with ID %s does not exist\n", backendId)
|
||||
} else {
|
||||
fmt.Fprintf(os.Stderr, "Failed to get config for backend %s: %q\n", backendId, err.Error())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println("Backend config:")
|
||||
printProperty("ID", backendConfig.Storage.ID)
|
||||
printProperty("Name", backendConfig.Storage.Name)
|
||||
printProperty("Issuer", backendConfig.OIDC.Issuer)
|
||||
printProperty("Client ID", backendConfig.OIDC.ClientID)
|
||||
printProperty("Client secret", backendConfig.OIDC.ClientSecret)
|
||||
}
|
||||
|
||||
func listBackends(backendService backend.Service) {
|
||||
backends, err := backendService.ListBackends()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to list backends: %q\n", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if len(backends) == 0 {
|
||||
fmt.Println("No backend configured")
|
||||
} else {
|
||||
for _, b := range backends {
|
||||
fmt.Printf("\t - backend %s: id %s issuer %s\n", b.Storage.Name, b.Storage.ID, b.OIDC.Issuer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
backendCmd.AddCommand(backendShowCmd)
|
||||
|
||||
// Here you will define your flags and configuration settings.
|
||||
|
||||
// Cobra supports Persistent Flags which will work for this command
|
||||
// and all subcommands, e.g.:
|
||||
// backendShowCmd.PersistentFlags().String("foo", "", "A help for foo")
|
||||
|
||||
// Cobra supports local flags which will only run when this command
|
||||
// is called directly, e.g.:
|
||||
// backendShowCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
||||
}
|
|
@ -64,7 +64,7 @@ func serve() {
|
|||
|
||||
logger.L.Info("Initializing authentication backends")
|
||||
|
||||
dex_server.ConnectorsConfig["refuseAll"] = func() dex_server.ConnectorConfig { return new(connector.RefuseAllConfig) }
|
||||
dex_server.ConnectorsConfig[connector.TypeRefuseAll] = func() dex_server.ConnectorConfig { return new(connector.RefuseAllConfig) }
|
||||
connectors, err := dexConf.Storage.ListConnectors()
|
||||
if err != nil {
|
||||
logger.L.Fatalf("Failed to get existing connectors: %s", err.Error())
|
||||
|
|
|
@ -8,6 +8,8 @@ import (
|
|||
"github.com/dexidp/dex/pkg/log"
|
||||
)
|
||||
|
||||
const TypeRefuseAll = "refuseAll"
|
||||
|
||||
type RefuseAllConfig struct{}
|
||||
|
||||
func (c *RefuseAllConfig) Open(id string, logger log.Logger) (connector.Connector, error) {
|
||||
|
|
86
polyculeconnect/services/backend/backend.go
Normal file
86
polyculeconnect/services/backend/backend.go
Normal file
|
@ -0,0 +1,86 @@
|
|||
package backend
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"git.faercol.me/faercol/polyculeconnect/polyculeconnect/connector"
|
||||
"github.com/dexidp/dex/connector/oidc"
|
||||
"github.com/dexidp/dex/storage"
|
||||
)
|
||||
|
||||
var ErrUnsupportedType = errors.New("unsupported connector type")
|
||||
|
||||
type BackendConfig struct {
|
||||
OIDC oidc.Config
|
||||
Storage storage.Connector
|
||||
}
|
||||
|
||||
func (bc *BackendConfig) FromConnector(connector storage.Connector) error {
|
||||
if connector.Type != "oidc" {
|
||||
return ErrUnsupportedType
|
||||
}
|
||||
if err := json.Unmarshal(connector.Config, &bc.OIDC); err != nil {
|
||||
return fmt.Errorf("invalid OIDC config: %w", err)
|
||||
}
|
||||
bc.Storage = connector
|
||||
return nil
|
||||
}
|
||||
|
||||
type Service interface {
|
||||
ListBackends() ([]BackendConfig, error)
|
||||
GetBackend(id string) (BackendConfig, error)
|
||||
AddBackend(config BackendConfig) error
|
||||
RemoveBackend(id string) error
|
||||
}
|
||||
|
||||
type concreteBackendService struct {
|
||||
s storage.Storage
|
||||
}
|
||||
|
||||
func (cbs *concreteBackendService) ListBackends() ([]BackendConfig, error) {
|
||||
connectors, err := cbs.s.ListConnectors()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get connectors from storage: %w", err)
|
||||
}
|
||||
var res []BackendConfig
|
||||
for _, c := range connectors {
|
||||
|
||||
// We know that this type is special, we don't want to use it at all here
|
||||
if c.Type == connector.TypeRefuseAll {
|
||||
continue
|
||||
}
|
||||
|
||||
var b BackendConfig
|
||||
if err := b.FromConnector(c); err != nil {
|
||||
return res, err
|
||||
}
|
||||
res = append(res, b)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (cbs *concreteBackendService) GetBackend(connectorID string) (BackendConfig, error) {
|
||||
c, err := cbs.s.GetConnector(connectorID)
|
||||
if err != nil {
|
||||
return BackendConfig{}, fmt.Errorf("failed to get connector from storage: %w", err)
|
||||
}
|
||||
var res BackendConfig
|
||||
if err := res.FromConnector(c); err != nil {
|
||||
return BackendConfig{}, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (cbs *concreteBackendService) AddBackend(config BackendConfig) error {
|
||||
return cbs.s.CreateConnector(config.Storage)
|
||||
}
|
||||
|
||||
func (cbs *concreteBackendService) RemoveBackend(connectorID string) error {
|
||||
return cbs.s.DeleteConnector(connectorID)
|
||||
}
|
||||
|
||||
func New(s storage.Storage) Service {
|
||||
return &concreteBackendService{s}
|
||||
}
|
42
polyculeconnect/services/backend/test/mock.go
Normal file
42
polyculeconnect/services/backend/test/mock.go
Normal file
|
@ -0,0 +1,42 @@
|
|||
package test
|
||||
|
||||
import "github.com/dexidp/dex/storage"
|
||||
|
||||
type MockBackendService struct {
|
||||
Connectors map[string]storage.Connector
|
||||
ListError error
|
||||
GetError error
|
||||
AddError error
|
||||
RemoveError error
|
||||
}
|
||||
|
||||
func (mbs *MockBackendService) ListConnectors() ([]storage.Connector, error) {
|
||||
var res []storage.Connector
|
||||
for _, c := range mbs.Connectors {
|
||||
res = append(res, c)
|
||||
}
|
||||
return res, mbs.ListError
|
||||
}
|
||||
|
||||
func (mbs *MockBackendService) GetConnector(connectorID string) (storage.Connector, error) {
|
||||
if res, ok := mbs.Connectors[connectorID]; ok {
|
||||
return res, mbs.GetError
|
||||
}
|
||||
return storage.Connector{}, storage.ErrNotFound
|
||||
}
|
||||
|
||||
func (mbs *MockBackendService) AddConnector(config storage.Connector) error {
|
||||
if _, ok := mbs.Connectors[config.ID]; ok {
|
||||
return storage.ErrAlreadyExists
|
||||
}
|
||||
if mbs.AddError != nil {
|
||||
return mbs.AddError
|
||||
}
|
||||
mbs.Connectors[config.ID] = config
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mbs *MockBackendService) RemoveConnector(connectorID string) error {
|
||||
delete(mbs.Connectors, connectorID)
|
||||
return mbs.RemoveError
|
||||
}
|
Loading…
Reference in a new issue