Commit 2e46f085 authored by Christian Pointner's avatar Christian Pointner
Browse files

auth: implemented backend state which allows delay initialization for oidc

parent 26bc97ab
Pipeline #552 passed with stages
in 11 minutes and 40 seconds
......@@ -27,6 +27,7 @@ package auth
import (
"context"
"encoding/json"
"errors"
"io/ioutil"
"log"
"net/http"
......@@ -59,13 +60,13 @@ func Init(c *Config, infoLog, errLog, dbgLog *log.Logger) (err error) {
dbgLog = log.New(ioutil.Discard, "", 0)
}
if c == nil {
infoLog.Println("authentication: disabled - will allow any request!")
return
}
auth.infoLog = infoLog
auth.errLog = errLog
auth.dbgLog = dbgLog
if c == nil {
auth.infoLog.Println("authentication: disabled - will allow any request!")
return
}
if auth.sessions, err = NewSessionManager(c.Sessions); err != nil {
auth.errLog.Printf("authentication: failed to initialize session manager: %v", err)
......@@ -74,21 +75,21 @@ func Init(c *Config, infoLog, errLog, dbgLog *log.Logger) (err error) {
if c.OIDC != nil {
if auth.backends.oidc, err = NewOIDCBackend(c.OIDC); err != nil {
auth.errLog.Printf("authentication: failed to initialize OIDC backend: %v", err)
auth.errLog.Printf("authentication: failed to enable OIDC backend: %v", err)
return
}
auth.infoLog.Printf("authentication: successfully initialized %s", auth.backends.oidc)
auth.infoLog.Printf("authentication: enabled %s", auth.backends.oidc)
}
if c.Passwd != nil {
if auth.backends.passwd, err = NewPasswdBackend(c.Passwd); err != nil {
auth.errLog.Printf("authentication: failed to initialize passwd backend: %v", err)
auth.errLog.Printf("authentication: failed to enable passwd backend: %v", err)
return
}
auth.infoLog.Printf("authentication: successfully initialized %s", auth.backends.passwd)
auth.infoLog.Printf("authentication: enabled %s", auth.backends.passwd)
}
auth.infoLog.Println("authentication: initialized")
auth.infoLog.Println("authentication: enabled")
return
}
......@@ -185,11 +186,13 @@ func listBackends(c *gin.Context) {
if auth.backends.oidc != nil {
backend := AuthBackendInfo{Name: "oidc"}
backend.Description = auth.backends.oidc.String()
backend.State = auth.backends.oidc.state
backends = append(backends, backend)
}
if auth.backends.passwd != nil {
backend := AuthBackendInfo{Name: "passwd"}
backend.Description = auth.backends.passwd.String()
backend.State = auth.backends.passwd.state
backends = append(backends, backend)
}
......@@ -243,7 +246,17 @@ func Middleware() gin.HandlerFunc {
}
}
func Healthz(ctx context.Context) error {
// TODO: implement this!
return nil
func Healthz(ctx context.Context) (err error) {
var messages []string
if auth.backends.oidc != nil && auth.backends.oidc.state.get() != BackendReady {
messages = append(messages, "oidc backend is enabled but not ready")
}
if auth.backends.passwd != nil && auth.backends.passwd.state.get() != BackendReady {
messages = append(messages, "passwd backend is enabled but not ready")
}
if len(messages) > 0 {
err = errors.New(strings.Join(messages, ", "))
}
return
}
......@@ -28,6 +28,7 @@ import (
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
"time"
......@@ -140,18 +141,34 @@ type OIDCBackend struct {
provider *oidc.Provider
verifier *oidc.IDTokenVerifier
oauth2Config oauth2.Config
state *BackendState
}
func NewOIDCBackend(cfg *OIDCConfig) (b *OIDCBackend, err error) {
// TODO: make ctx a parameter?
ctx := context.Background()
b = &OIDCBackend{issuerURL: cfg.IssuerURL, loginTimeout: cfg.LoginTimeout}
if b.loginTimeout <= 0 {
b.loginTimeout = defaultLoginTimeout
}
state := BackendNew
b.state = &state
if b.provider, err = oidc.NewProvider(ctx, cfg.IssuerURL); err != nil {
return
go b.initialize(cfg)
return
}
func (b *OIDCBackend) initialize(cfg *OIDCConfig) {
b.state.set(BackendInitializing)
for {
var err error
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) // TODO: hardcoded value
b.provider, err = oidc.NewProvider(ctx, cfg.IssuerURL)
cancel()
if err == nil {
break
}
auth.errLog.Printf("authentication/oidc: initialization failed: %v, will retry...", err)
time.Sleep(10 * time.Second) // TODO: hardcoded value
}
oidcConfig := &oidc.Config{
......@@ -167,14 +184,19 @@ func NewOIDCBackend(cfg *OIDCConfig) (b *OIDCBackend, err error) {
Scopes: []string{oidc.ScopeOpenID, "username", "aura_shows"},
}
return
b.state.set(BackendReady)
auth.infoLog.Printf("authentication/oidc: initialization complete, backend is now %s", b.state.String())
}
func (b *OIDCBackend) String() string {
return "OpenID Connect using Identity Provider: " + b.issuerURL
return fmt.Sprintf("OpenID Connect using Identity Provider: %s", b.issuerURL)
}
func (b *OIDCBackend) NewSession(ctx context.Context, arguments json.RawMessage) (s *Session, err error) {
if b.state.get() != BackendReady {
return nil, errors.New("oidc backend is not ready")
}
var oauth2Token *oauth2.Token
if arguments != nil {
oauth2Token = &oauth2.Token{}
......
......@@ -33,10 +33,13 @@ import (
type PasswdBackend struct {
userDB map[string]*PasswdUserConfig
state *BackendState
}
func NewPasswdBackend(userDB map[string]*PasswdUserConfig) (b *PasswdBackend, err error) {
b = &PasswdBackend{userDB: userDB}
state := BackendReady
b.state = &state
return
}
......@@ -45,6 +48,10 @@ func (b *PasswdBackend) String() string {
}
func (b *PasswdBackend) NewSession(ctx context.Context, arguments json.RawMessage) (s *Session, err error) {
if b.state.get() != BackendReady {
return nil, errors.New("password backend is not ready")
}
if arguments == nil {
return nil, errors.New("mandatory arguments missing")
}
......
......@@ -29,6 +29,7 @@ import (
"encoding/base64"
"encoding/json"
"net/http"
"sync/atomic"
"github.com/gin-gonic/gin"
)
......@@ -47,9 +48,49 @@ func generateRandomString(len int) (string, error) {
return base64.RawURLEncoding.EncodeToString(b[:]), nil
}
type BackendState uint32
const (
BackendNew BackendState = iota
BackendInitializing
BackendReady
BackendFailed
BackendDestroyed
)
func (s *BackendState) set(state BackendState) {
atomic.StoreUint32((*uint32)(s), uint32(state))
}
func (s *BackendState) get() (state BackendState) {
return BackendState(atomic.LoadUint32((*uint32)(s)))
}
func (s *BackendState) String() string {
switch s.get() {
case BackendNew:
return "new"
case BackendInitializing:
return "initializing"
case BackendReady:
return "ready"
case BackendFailed:
return "failed"
case BackendDestroyed:
return "destroyed"
}
return "unknown"
}
func (s *BackendState) MarshalText() (data []byte, err error) {
data = []byte(s.String())
return
}
type AuthBackendInfo struct {
Name string
Description string
State *BackendState
}
type NewSessionRequest struct {
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment