mirror of
https://github.com/nais/wonderwall.git
synced 2026-08-23 21:16:14 +00:00
feat: initial skeleton setup for SSO mode
This commit is contained in:
+42
-15
@@ -18,6 +18,13 @@ import (
|
||||
"github.com/nais/wonderwall/pkg/server"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := run()
|
||||
if err != nil {
|
||||
log.Fatalf("Fatal error: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func run() error {
|
||||
cfg, err := config.Initialize()
|
||||
if err != nil {
|
||||
@@ -29,28 +36,29 @@ func run() error {
|
||||
return err
|
||||
}
|
||||
|
||||
openidConfig, err := openidconfig.NewConfig(cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
crypt := crypto.NewCrypter(key)
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
crypt := crypto.NewCrypter(key)
|
||||
var h router.Source
|
||||
|
||||
cookieOpts := cookie.DefaultOptions()
|
||||
|
||||
jwksProvider, err := provider.NewJwksProvider(ctx, openidConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
if cfg.SSO.Enabled {
|
||||
switch cfg.SSO.Mode {
|
||||
case config.SSOModeServer:
|
||||
h, err = ssoServerHandler()
|
||||
case config.SSOModeProxy:
|
||||
h, err = ssoProxyHandler()
|
||||
default:
|
||||
return fmt.Errorf("invalid SSO mode: %q", cfg.SSO.Mode)
|
||||
}
|
||||
} else {
|
||||
h, err = standaloneHandler(ctx, cfg, crypt)
|
||||
}
|
||||
|
||||
h, err := handler.NewHandler(cfg, cookieOpts, jwksProvider, openidConfig, crypt)
|
||||
if err != nil {
|
||||
return fmt.Errorf("initializing routing handler: %w", err)
|
||||
}
|
||||
|
||||
r := router.New(h)
|
||||
|
||||
go func() {
|
||||
@@ -62,9 +70,28 @@ func run() error {
|
||||
return server.Start(cfg, r)
|
||||
}
|
||||
|
||||
func main() {
|
||||
err := run()
|
||||
func standaloneHandler(ctx context.Context, cfg *config.Config, crypt crypto.Crypter) (router.Source, error) {
|
||||
openidConfig, err := openidconfig.NewConfig(cfg)
|
||||
if err != nil {
|
||||
log.Fatalf("Fatal error: %s", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
jwksProvider, err := provider.NewJwksProvider(ctx, openidConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cookieOpts := cookie.DefaultOptions()
|
||||
|
||||
return handler.NewHandler(cfg, cookieOpts, jwksProvider, openidConfig, crypt)
|
||||
}
|
||||
|
||||
func ssoServerHandler() (router.Source, error) {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func ssoProxyHandler() (router.Source, error) {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/nais/liberator/pkg/conftools"
|
||||
@@ -30,6 +31,7 @@ type Config struct {
|
||||
Redis Redis `json:"redis"`
|
||||
|
||||
Loginstatus Loginstatus `json:"loginstatus"`
|
||||
SSO SSO `json:"sso"`
|
||||
}
|
||||
|
||||
type Loginstatus struct {
|
||||
@@ -47,6 +49,20 @@ type Session struct {
|
||||
Refresh bool `json:"refresh"`
|
||||
}
|
||||
|
||||
type SSO struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
Domain string `json:"domain"`
|
||||
Mode SSOMode `json:"mode"`
|
||||
ServerURL string `json:"server-url"`
|
||||
}
|
||||
|
||||
type SSOMode string
|
||||
|
||||
const (
|
||||
SSOModeServer SSOMode = "server"
|
||||
SSOModeProxy SSOMode = "proxy"
|
||||
)
|
||||
|
||||
const (
|
||||
BindAddress = "bind-address"
|
||||
LogFormat = "log-format"
|
||||
@@ -70,6 +86,11 @@ const (
|
||||
LoginstatusCookieName = "loginstatus.cookie-name"
|
||||
LoginstatusResourceIndicator = "loginstatus.resource-indicator"
|
||||
LoginstatusTokenURL = "loginstatus.token-url"
|
||||
|
||||
SSOEnabled = "sso.enabled"
|
||||
SSODomain = "sso.domain"
|
||||
SSOModeFlag = "sso.mode"
|
||||
SSOServerURL = "sso.server-url"
|
||||
)
|
||||
|
||||
func Initialize() (*Config, error) {
|
||||
@@ -98,6 +119,11 @@ func Initialize() (*Config, error) {
|
||||
flag.String(LoginstatusResourceIndicator, "", "The resource indicator that should be included in the authorization request to get an audience-restricted token that Loginstatus accepts. Empty means no resource indicator.")
|
||||
flag.String(LoginstatusTokenURL, "", "The URL to the Loginstatus service that returns an opaque token.")
|
||||
|
||||
flag.Bool(SSOEnabled, false, "Enable single sign-on mode; one server acting as the OIDC Relying Party, and N proxies. The proxies delegate most endpoint operations to the server, and only implements a reverse proxy that reads the user's session data from the shared store.")
|
||||
flag.String(SSODomain, "", "The domain that the session cookies should be set for, usually the second-level domain name (e.g. example.com).")
|
||||
flag.String(SSOModeFlag, string(SSOModeServer), "The SSO mode for this instance. Must be one of 'server' or 'proxy'.")
|
||||
flag.String(SSOServerURL, "", "The URL that points to the SSO server instance.")
|
||||
|
||||
redisFlags()
|
||||
openIDFlags()
|
||||
|
||||
@@ -157,5 +183,25 @@ func (c *Config) Validate() error {
|
||||
return fmt.Errorf("%q cannot be enabled without %q", SessionInactivity, SessionRefresh)
|
||||
}
|
||||
|
||||
if c.SSO.Enabled {
|
||||
switch c.SSO.Mode {
|
||||
case SSOModeProxy:
|
||||
if len(c.SSO.ServerURL) == 0 {
|
||||
return fmt.Errorf("%q cannot be empty", SSOServerURL)
|
||||
}
|
||||
|
||||
_, err := url.ParseRequestURI(c.SSO.ServerURL)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%q must be a valid url: %w", SSOServerURL, err)
|
||||
}
|
||||
case SSOModeServer:
|
||||
if len(c.SSO.Domain) == 0 {
|
||||
return fmt.Errorf("%q cannot be empty", SSODomain)
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("%q must be one of [%q, %q]", SSOModeFlag, SSOModeServer, SSOModeProxy)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/nais/wonderwall/pkg/ingress"
|
||||
"github.com/nais/wonderwall/pkg/router"
|
||||
)
|
||||
|
||||
var _ router.Source = &SSOProxyHandler{}
|
||||
|
||||
type SSOProxyHandler struct {
|
||||
}
|
||||
|
||||
func (s *SSOProxyHandler) Login(w http.ResponseWriter, r *http.Request) {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (s *SSOProxyHandler) LoginCallback(w http.ResponseWriter, r *http.Request) {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (s *SSOProxyHandler) Logout(w http.ResponseWriter, r *http.Request) {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (s *SSOProxyHandler) LogoutCallback(w http.ResponseWriter, r *http.Request) {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (s *SSOProxyHandler) LogoutFrontChannel(w http.ResponseWriter, r *http.Request) {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (s *SSOProxyHandler) LogoutLocal(w http.ResponseWriter, r *http.Request) {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (s *SSOProxyHandler) Session(w http.ResponseWriter, r *http.Request) {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (s *SSOProxyHandler) SessionRefresh(w http.ResponseWriter, r *http.Request) {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (s *SSOProxyHandler) ReverseProxy(w http.ResponseWriter, r *http.Request) {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (s *SSOProxyHandler) GetIngresses() *ingress.Ingresses {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (s *SSOProxyHandler) GetProviderName() string {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/nais/wonderwall/pkg/ingress"
|
||||
"github.com/nais/wonderwall/pkg/router"
|
||||
)
|
||||
|
||||
var _ router.Source = &SSOServerHandler{}
|
||||
|
||||
type SSOServerHandler struct {
|
||||
}
|
||||
|
||||
func (s *SSOServerHandler) Login(w http.ResponseWriter, r *http.Request) {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (s *SSOServerHandler) LoginCallback(w http.ResponseWriter, r *http.Request) {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (s *SSOServerHandler) Logout(w http.ResponseWriter, r *http.Request) {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (s *SSOServerHandler) LogoutCallback(w http.ResponseWriter, r *http.Request) {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (s *SSOServerHandler) LogoutFrontChannel(w http.ResponseWriter, r *http.Request) {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (s *SSOServerHandler) LogoutLocal(w http.ResponseWriter, r *http.Request) {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (s *SSOServerHandler) Session(w http.ResponseWriter, r *http.Request) {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (s *SSOServerHandler) SessionRefresh(w http.ResponseWriter, r *http.Request) {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (s *SSOServerHandler) ReverseProxy(w http.ResponseWriter, r *http.Request) {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (s *SSOServerHandler) GetIngresses() *ingress.Ingresses {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (s *SSOServerHandler) GetProviderName() string {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
Reference in New Issue
Block a user