Checkpoint: auth, engine, seal, server, grpc updates
Co-authored-by: Junie <junie@jetbrains.com>
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"log/slog"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -34,29 +35,35 @@ type cachedClaims struct {
|
||||
// Authenticator provides MCIAS-backed authentication.
|
||||
type Authenticator struct {
|
||||
client *mcias.Client
|
||||
logger *slog.Logger
|
||||
|
||||
mu sync.RWMutex
|
||||
cache map[string]*cachedClaims // keyed by SHA-256(token)
|
||||
}
|
||||
|
||||
// NewAuthenticator creates a new authenticator with the given MCIAS client.
|
||||
func NewAuthenticator(client *mcias.Client) *Authenticator {
|
||||
func NewAuthenticator(client *mcias.Client, logger *slog.Logger) *Authenticator {
|
||||
return &Authenticator{
|
||||
client: client,
|
||||
logger: logger,
|
||||
cache: make(map[string]*cachedClaims),
|
||||
}
|
||||
}
|
||||
|
||||
// Login authenticates a user via MCIAS and returns the token.
|
||||
func (a *Authenticator) Login(username, password, totpCode string) (token string, expiresAt string, err error) {
|
||||
a.logger.Debug("login attempt", "username", username)
|
||||
tok, exp, err := a.client.Login(username, password, totpCode)
|
||||
if err != nil {
|
||||
var authErr *mcias.MciasAuthError
|
||||
if errors.As(err, &authErr) {
|
||||
a.logger.Debug("login failed: invalid credentials", "username", username)
|
||||
return "", "", ErrInvalidCredentials
|
||||
}
|
||||
a.logger.Debug("login failed", "username", username, "error", err)
|
||||
return "", "", err
|
||||
}
|
||||
a.logger.Debug("login succeeded", "username", username)
|
||||
return tok, exp, nil
|
||||
}
|
||||
|
||||
@@ -69,15 +76,19 @@ func (a *Authenticator) ValidateToken(token string) (*TokenInfo, error) {
|
||||
cached, ok := a.cache[key]
|
||||
a.mu.RUnlock()
|
||||
if ok && time.Now().Before(cached.expiresAt) {
|
||||
a.logger.Debug("token validated from cache")
|
||||
return cached.info, nil
|
||||
}
|
||||
|
||||
a.logger.Debug("validating token with MCIAS")
|
||||
// Validate with MCIAS.
|
||||
claims, err := a.client.ValidateToken(token)
|
||||
if err != nil {
|
||||
a.logger.Debug("token validation failed", "error", err)
|
||||
return nil, err
|
||||
}
|
||||
if !claims.Valid {
|
||||
a.logger.Debug("token invalid per MCIAS")
|
||||
return nil, ErrInvalidToken
|
||||
}
|
||||
|
||||
@@ -94,6 +105,7 @@ func (a *Authenticator) ValidateToken(token string) (*TokenInfo, error) {
|
||||
expiresAt: time.Now().Add(tokenCacheTTL),
|
||||
}
|
||||
a.mu.Unlock()
|
||||
a.logger.Debug("token validated and cached", "username", info.Username, "is_admin", info.IsAdmin)
|
||||
|
||||
return info, nil
|
||||
}
|
||||
@@ -105,6 +117,7 @@ func (a *Authenticator) Logout(client *mcias.Client) error {
|
||||
|
||||
// ClearCache removes all cached token validations.
|
||||
func (a *Authenticator) ClearCache() {
|
||||
a.logger.Debug("clearing token cache")
|
||||
a.mu.Lock()
|
||||
a.cache = make(map[string]*cachedClaims)
|
||||
a.mu.Unlock()
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -33,7 +34,7 @@ func TestHasAdminRole(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNewAuthenticator(t *testing.T) {
|
||||
a := NewAuthenticator(nil)
|
||||
a := NewAuthenticator(nil, slog.Default())
|
||||
if a == nil {
|
||||
t.Fatal("NewAuthenticator returned nil")
|
||||
}
|
||||
@@ -43,7 +44,7 @@ func TestNewAuthenticator(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestClearCache(t *testing.T) {
|
||||
a := NewAuthenticator(nil)
|
||||
a := NewAuthenticator(nil, slog.Default())
|
||||
a.cache["test"] = &cachedClaims{info: &TokenInfo{Username: "test"}}
|
||||
a.ClearCache()
|
||||
if len(a.cache) != 0 {
|
||||
|
||||
Reference in New Issue
Block a user