feat: initial skeleton setup for SSO mode

This commit is contained in:
Trong Huu Nguyen
2023-02-10 14:57:56 +01:00
parent bd748b9cef
commit 3d08d0b4b0
4 changed files with 224 additions and 15 deletions
+42 -15
View File
@@ -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")
}
+46
View File
@@ -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
}
+68
View File
@@ -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")
}
+68
View File
@@ -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")
}