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")
|
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()
|
connectors, err := dexConf.Storage.ListConnectors()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.L.Fatalf("Failed to get existing connectors: %s", err.Error())
|
logger.L.Fatalf("Failed to get existing connectors: %s", err.Error())
|
||||||
|
|
|
@ -8,6 +8,8 @@ import (
|
||||||
"github.com/dexidp/dex/pkg/log"
|
"github.com/dexidp/dex/pkg/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const TypeRefuseAll = "refuseAll"
|
||||||
|
|
||||||
type RefuseAllConfig struct{}
|
type RefuseAllConfig struct{}
|
||||||
|
|
||||||
func (c *RefuseAllConfig) Open(id string, logger log.Logger) (connector.Connector, error) {
|
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