Add ability to provision OIDC providers from ENV
PROVISION_OIDC_PROVIDER="name provider-url"
This commit is contained in:
@@ -2,12 +2,16 @@ package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/titpetric/factory"
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/pkg/cli"
|
||||
"github.com/cortezaproject/corteza-server/pkg/cli/options"
|
||||
"github.com/cortezaproject/corteza-server/system/internal/auth/external"
|
||||
"github.com/cortezaproject/corteza-server/system/internal/repository"
|
||||
"github.com/cortezaproject/corteza-server/system/internal/service"
|
||||
"github.com/cortezaproject/corteza-server/system/types"
|
||||
@@ -77,3 +81,49 @@ func makeDefaultApplications(ctx context.Context, cmd *cobra.Command, c *cli.Con
|
||||
func discoverSettings(ctx context.Context, cmd *cobra.Command, c *cli.Config) error {
|
||||
return service.DefaultSettings.With(ctx).AutoDiscovery()
|
||||
}
|
||||
|
||||
func oidcAutoDiscovery(ctx context.Context, cmd *cobra.Command, c *cli.Config) (err error) {
|
||||
var provider = strings.TrimSpace(options.EnvString("", "PROVISION_OIDC_PROVIDER", ""))
|
||||
|
||||
c.Log.Debug("OIDC auto discovery provision", zap.String("providers", provider))
|
||||
|
||||
if len(provider) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
providers = strings.Split(provider, " ")
|
||||
plen = len(providers)
|
||||
name, purl string
|
||||
eap *service.AuthSettingsExternalAuthProvider
|
||||
)
|
||||
|
||||
if plen%2 == 1 {
|
||||
return errors.New("expecting even number of providers")
|
||||
}
|
||||
|
||||
for p := 0; p < plen; p = p + 2 {
|
||||
name, purl = providers[p], providers[p+1]
|
||||
|
||||
eap, err = external.RegisterOidcProvider(ctx, name, purl, false, true, true)
|
||||
|
||||
if err != nil {
|
||||
c.Log.Error(
|
||||
"could not register oidc provider",
|
||||
zap.String("url", purl),
|
||||
zap.String("name", name),
|
||||
zap.Error(err))
|
||||
return
|
||||
} else if eap == nil {
|
||||
c.Log.Info("provider already exists",
|
||||
zap.String("name", name))
|
||||
} else {
|
||||
c.Log.Info("provider successfuly registered",
|
||||
zap.String("url", purl),
|
||||
zap.String("key", eap.Key),
|
||||
zap.String("name", name))
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
+7
-55
@@ -2,16 +2,13 @@ package commands
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/titpetric/factory"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/internal/auth"
|
||||
"github.com/cortezaproject/corteza-server/internal/settings"
|
||||
"github.com/cortezaproject/corteza-server/pkg/cli"
|
||||
"github.com/cortezaproject/corteza-server/system/internal/auth/external"
|
||||
"github.com/cortezaproject/corteza-server/system/internal/repository"
|
||||
@@ -38,45 +35,17 @@ func Auth(ctx context.Context, c *cli.Config) *cobra.Command {
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
c.InitServices(ctx, c)
|
||||
|
||||
var (
|
||||
name, providerUrl = args[0], args[1]
|
||||
es = service.DefaultAuthSettings
|
||||
_, err := external.RegisterOidcProvider(
|
||||
ctx,
|
||||
args[0],
|
||||
args[1],
|
||||
true,
|
||||
!skipValidationOnAutoDiscoveredProvider,
|
||||
enableDiscoveredProvider,
|
||||
)
|
||||
|
||||
if !skipValidationOnAutoDiscoveredProvider {
|
||||
// Do basic validation of external auth settings
|
||||
// will fail if secret or url are not set
|
||||
cli.HandleError(es.StaticValidateExternal())
|
||||
|
||||
// Do full rediredct-URL check
|
||||
cli.HandleError(es.ValidateExternalRedirectURL())
|
||||
}
|
||||
|
||||
p, err := parseExternalProviderUrl(providerUrl)
|
||||
cli.HandleError(err)
|
||||
|
||||
eap, err := external.RegisterNewOpenIdClient(ctx, es, name, p.String())
|
||||
cli.HandleError(err)
|
||||
|
||||
vv, err := eap.MakeValueSet("openid-connect." + name)
|
||||
cli.HandleError(err)
|
||||
|
||||
if enableDiscoveredProvider {
|
||||
cli.HandleError(vv.Walk(func(value *settings.Value) error {
|
||||
if strings.HasSuffix(value.Name, ".enabled") {
|
||||
return value.SetValue(true)
|
||||
}
|
||||
|
||||
return nil
|
||||
}))
|
||||
|
||||
v := &settings.Value{Name: "auth.external.enabled"}
|
||||
cli.HandleError(v.SetValue(true))
|
||||
vv = append(vv, v)
|
||||
}
|
||||
|
||||
cli.HandleError(service.DefaultIntSettings.BulkSet(vv))
|
||||
|
||||
if enableDiscoveredProvider {
|
||||
cmd.Println("OIDC provider successfully added and enabled.")
|
||||
} else {
|
||||
@@ -171,20 +140,3 @@ func Auth(ctx context.Context, c *cli.Config) *cobra.Command {
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func parseExternalProviderUrl(in string) (p *url.URL, err error) {
|
||||
if i := strings.Index(in, "://"); i == -1 {
|
||||
// Add schema if missing
|
||||
in = "https://" + in
|
||||
}
|
||||
|
||||
if p, err = url.Parse(in); err != nil {
|
||||
// Try to parse it
|
||||
return
|
||||
} else if i := strings.Index(p.Path, external.WellKnown); i > -1 {
|
||||
// Cut off well-known-path
|
||||
p.Path = p.Path[:i]
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
+4
@@ -7,6 +7,10 @@ import (
|
||||
"github.com/cortezaproject/corteza-server/system/internal/service"
|
||||
)
|
||||
|
||||
const (
|
||||
OIDC_PROVIDER_PREFIX = "openid-connect."
|
||||
)
|
||||
|
||||
func Init() {
|
||||
setupGoth(service.DefaultAuthSettings)
|
||||
}
|
||||
|
||||
+2
-4
@@ -1,11 +1,9 @@
|
||||
package commands
|
||||
package external
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/system/internal/auth/external"
|
||||
)
|
||||
|
||||
func Test_parseExternalProviderUrl(t *testing.T) {
|
||||
@@ -48,7 +46,7 @@ func Test_parseExternalProviderUrl(t *testing.T) {
|
||||
},
|
||||
{
|
||||
"add schema and remove well-known",
|
||||
args{"cortezaproject.org/some-subdir/" + external.WellKnown},
|
||||
args{"cortezaproject.org/some-subdir/" + WellKnown},
|
||||
mustParseURL("https://cortezaproject.org/some-subdir/"),
|
||||
false,
|
||||
},
|
||||
+1
-1
@@ -77,7 +77,7 @@ func setupGothProviders(as service.AuthSettings) {
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.Index(name, "openid-connect.") == 0 {
|
||||
if strings.Index(name, OIDC_PROVIDER_PREFIX) == 0 {
|
||||
if pc.IssuerUrl == "" {
|
||||
log.Error("failed to discover OIDC provider, URL empty")
|
||||
continue
|
||||
|
||||
+90
-2
@@ -3,19 +3,22 @@ package external
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/crusttech/go-oidc"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/internal/settings"
|
||||
"github.com/cortezaproject/corteza-server/system/internal/service"
|
||||
)
|
||||
|
||||
// @todo remove dependency on github.com/crusttech/go-oidc (and github.com/coreos/go-oidc)
|
||||
// and move client registration to corteza codebase
|
||||
func RegisterNewOpenIdClient(ctx context.Context, eas service.AuthSettings, name, url string) (eap *service.AuthSettingsExternalAuthProvider, err error) {
|
||||
func DiscoverOidcProvider(ctx context.Context, eas service.AuthSettings, name, url string) (eap *service.AuthSettingsExternalAuthProvider, err error) {
|
||||
var (
|
||||
provider *oidc.Provider
|
||||
client *oidc.Client
|
||||
redirectUrl = fmt.Sprintf(eas.ExternalRedirectUrl, "openid-connect."+name)
|
||||
redirectUrl = fmt.Sprintf(eas.ExternalRedirectUrl, OIDC_PROVIDER_PREFIX+name)
|
||||
)
|
||||
|
||||
if provider, err = oidc.NewProvider(ctx, url); err != nil {
|
||||
@@ -41,3 +44,88 @@ func RegisterNewOpenIdClient(ctx context.Context, eas service.AuthSettings, name
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func RegisterOidcProvider(ctx context.Context, name, providerUrl string, force, validate, enable bool) (eap *service.AuthSettingsExternalAuthProvider, err error) {
|
||||
var (
|
||||
as = service.DefaultAuthSettings
|
||||
)
|
||||
|
||||
if !force {
|
||||
if _, exists := as.ExternalProviders[OIDC_PROVIDER_PREFIX+name]; exists {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if validate {
|
||||
// Do basic validation of external auth settings
|
||||
// will fail if secret or url are not set
|
||||
if err = as.StaticValidateExternal(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Do full rediredct-URL check
|
||||
if err = as.ValidateExternalRedirectURL(); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
p, err := parseExternalProviderUrl(providerUrl)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
eap, err = DiscoverOidcProvider(ctx, as, name, p.String())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
vv, err := eap.MakeValueSet(OIDC_PROVIDER_PREFIX + name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if enable {
|
||||
err = vv.Walk(func(value *settings.Value) error {
|
||||
if strings.HasSuffix(value.Name, ".enabled") {
|
||||
return value.SetValue(true)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
v := &settings.Value{Name: "auth.external.enabled"}
|
||||
err = v.SetValue(true)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
vv = append(vv, v)
|
||||
}
|
||||
|
||||
err = service.DefaultIntSettings.BulkSet(vv)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func parseExternalProviderUrl(in string) (p *url.URL, err error) {
|
||||
if i := strings.Index(in, "://"); i == -1 {
|
||||
// Add schema if missing
|
||||
in = "https://" + in
|
||||
}
|
||||
|
||||
if p, err = url.Parse(in); err != nil {
|
||||
// Try to parse it
|
||||
return
|
||||
} else if i := strings.Index(p.Path, WellKnown); i > -1 {
|
||||
// Cut off well-known-path
|
||||
p.Path = p.Path[:i]
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -56,6 +56,7 @@ func Configure() *cli.Config {
|
||||
cli.HandleError(accessControlSetup(ctx, cmd, c))
|
||||
cli.HandleError(makeDefaultApplications(ctx, cmd, c))
|
||||
cli.HandleError(discoverSettings(ctx, cmd, c))
|
||||
cli.HandleError(oidcAutoDiscovery(ctx, cmd, c))
|
||||
|
||||
// Run auto configuration
|
||||
commands.SettingsAutoConfigure(cmd)
|
||||
|
||||
Reference in New Issue
Block a user