Cleanup and adapt to new structure

This commit is contained in:
Jan Eitzinger 2023-08-17 12:34:30 +02:00
parent c7a04328d9
commit 15231bc683
9 changed files with 89 additions and 60 deletions

View File

@ -211,7 +211,7 @@ func main() {
var authentication *auth.Authentication var authentication *auth.Authentication
if !config.Keys.DisableAuthentication { if !config.Keys.DisableAuthentication {
var err error var err error
if authentication, err = auth.Init(db.DB, map[string]interface{}{ if authentication, err = auth.Init(map[string]interface{}{
"ldap": config.Keys.LdapConfig, "ldap": config.Keys.LdapConfig,
"jwt": config.Keys.JwtConfig, "jwt": config.Keys.JwtConfig,
}); err != nil { }); err != nil {

View File

@ -18,17 +18,15 @@ import (
"github.com/ClusterCockpit/cc-backend/pkg/log" "github.com/ClusterCockpit/cc-backend/pkg/log"
"github.com/ClusterCockpit/cc-backend/pkg/schema" "github.com/ClusterCockpit/cc-backend/pkg/schema"
"github.com/gorilla/sessions" "github.com/gorilla/sessions"
"github.com/jmoiron/sqlx"
) )
type Authenticator interface { type Authenticator interface {
Init(auth *Authentication, config interface{}) error Init(config interface{}) error
CanLogin(user *schema.User, username string, rw http.ResponseWriter, r *http.Request) bool CanLogin(user *schema.User, username string, rw http.ResponseWriter, r *http.Request) bool
Login(user *schema.User, rw http.ResponseWriter, r *http.Request) (*schema.User, error) Login(user *schema.User, rw http.ResponseWriter, r *http.Request) (*schema.User, error)
} }
type Authentication struct { type Authentication struct {
db *sqlx.DB
sessionStore *sessions.CookieStore sessionStore *sessions.CookieStore
SessionMaxAge time.Duration SessionMaxAge time.Duration
@ -82,10 +80,8 @@ func (auth *Authentication) AuthViaSession(
}, nil }, nil
} }
func Init(db *sqlx.DB, func Init(configs map[string]interface{}) (*Authentication, error) {
configs map[string]interface{}) (*Authentication, error) {
auth := &Authentication{} auth := &Authentication{}
auth.db = db
sessKey := os.Getenv("SESSION_KEY") sessKey := os.Getenv("SESSION_KEY")
if sessKey == "" { if sessKey == "" {
@ -106,14 +102,14 @@ func Init(db *sqlx.DB,
} }
auth.JwtAuth = &JWTAuthenticator{} auth.JwtAuth = &JWTAuthenticator{}
if err := auth.JwtAuth.Init(auth, configs["jwt"]); err != nil { if err := auth.JwtAuth.Init(configs["jwt"]); err != nil {
log.Error("Error while initializing authentication -> jwtAuth init failed") log.Error("Error while initializing authentication -> jwtAuth init failed")
return nil, err return nil, err
} }
if config, ok := configs["ldap"]; ok { if config, ok := configs["ldap"]; ok {
ldapAuth := &LdapAuthenticator{} ldapAuth := &LdapAuthenticator{}
if err := ldapAuth.Init(auth, config); err != nil { if err := ldapAuth.Init(config); err != nil {
log.Warn("Error while initializing authentication -> ldapAuth init failed") log.Warn("Error while initializing authentication -> ldapAuth init failed")
} else { } else {
auth.LdapAuth = ldapAuth auth.LdapAuth = ldapAuth
@ -122,21 +118,21 @@ func Init(db *sqlx.DB,
} }
jwtSessionAuth := &JWTSessionAuthenticator{} jwtSessionAuth := &JWTSessionAuthenticator{}
if err := jwtSessionAuth.Init(auth, configs["jwt"]); err != nil { if err := jwtSessionAuth.Init(configs["jwt"]); err != nil {
log.Warn("Error while initializing authentication -> jwtSessionAuth init failed") log.Warn("Error while initializing authentication -> jwtSessionAuth init failed")
} else { } else {
auth.authenticators = append(auth.authenticators, jwtSessionAuth) auth.authenticators = append(auth.authenticators, jwtSessionAuth)
} }
jwtCookieSessionAuth := &JWTCookieSessionAuthenticator{} jwtCookieSessionAuth := &JWTCookieSessionAuthenticator{}
if err := jwtCookieSessionAuth.Init(auth, configs["jwt"]); err != nil { if err := jwtCookieSessionAuth.Init(configs["jwt"]); err != nil {
log.Warn("Error while initializing authentication -> jwtCookieSessionAuth init failed") log.Warn("Error while initializing authentication -> jwtCookieSessionAuth init failed")
} else { } else {
auth.authenticators = append(auth.authenticators, jwtCookieSessionAuth) auth.authenticators = append(auth.authenticators, jwtCookieSessionAuth)
} }
auth.LocalAuth = &LocalAuthenticator{} auth.LocalAuth = &LocalAuthenticator{}
if err := auth.LocalAuth.Init(auth, nil); err != nil { if err := auth.LocalAuth.Init(nil); err != nil {
log.Error("Error while initializing authentication -> localAuth init failed") log.Error("Error while initializing authentication -> localAuth init failed")
return nil, err return nil, err
} }
@ -150,13 +146,12 @@ func (auth *Authentication) Login(
onfailure func(rw http.ResponseWriter, r *http.Request, loginErr error)) http.Handler { onfailure func(rw http.ResponseWriter, r *http.Request, loginErr error)) http.Handler {
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
ur := repository.GetUserRepository()
err := errors.New("no authenticator applied") err := errors.New("no authenticator applied")
username := r.FormValue("username") username := r.FormValue("username")
dbUser := (*schema.User)(nil) dbUser := (*schema.User)(nil)
if username != "" { if username != "" {
dbUser, err = ur.GetUser(username) dbUser, err = repository.GetUserRepository().GetUser(username)
if err != nil && err != sql.ErrNoRows { if err != nil && err != sql.ErrNoRows {
log.Errorf("Error while loading user '%v'", username) log.Errorf("Error while loading user '%v'", username)
} }
@ -166,10 +161,6 @@ func (auth *Authentication) Login(
if !authenticator.CanLogin(dbUser, username, rw, r) { if !authenticator.CanLogin(dbUser, username, rw, r) {
continue continue
} }
dbUser, err = ur.GetUser(username)
if err != nil && err != sql.ErrNoRows {
log.Errorf("Error while loading user '%v'", username)
}
user, err := authenticator.Login(dbUser, rw, r) user, err := authenticator.Login(dbUser, rw, r)
if err != nil { if err != nil {
@ -197,14 +188,6 @@ func (auth *Authentication) Login(
return return
} }
if dbUser == nil {
if err := ur.AddUser(user); err != nil {
// TODO Add AuthSource
log.Errorf("Error while adding user '%v' to auth from XX",
user.Username)
}
}
log.Infof("login successfull: user: %#v (roles: %v, projects: %v)", user.Username, user.Roles, user.Projects) log.Infof("login successfull: user: %#v (roles: %v, projects: %v)", user.Username, user.Roles, user.Projects)
ctx := context.WithValue(r.Context(), repository.ContextUserKey, user) ctx := context.WithValue(r.Context(), repository.ContextUserKey, user)
onsuccess.ServeHTTP(rw, r.WithContext(ctx)) onsuccess.ServeHTTP(rw, r.WithContext(ctx))

View File

@ -25,7 +25,7 @@ type JWTAuthenticator struct {
config *schema.JWTAuthConfig config *schema.JWTAuthConfig
} }
func (ja *JWTAuthenticator) Init(auth *Authentication, conf interface{}) error { func (ja *JWTAuthenticator) Init(conf interface{}) error {
ja.config = conf.(*schema.JWTAuthConfig) ja.config = conf.(*schema.JWTAuthConfig)
pubKey, privKey := os.Getenv("JWT_PUBLIC_KEY"), os.Getenv("JWT_PRIVATE_KEY") pubKey, privKey := os.Getenv("JWT_PUBLIC_KEY"), os.Getenv("JWT_PRIVATE_KEY")

View File

@ -17,8 +17,6 @@ import (
) )
type JWTCookieSessionAuthenticator struct { type JWTCookieSessionAuthenticator struct {
auth *Authentication
publicKey ed25519.PublicKey publicKey ed25519.PublicKey
privateKey ed25519.PrivateKey privateKey ed25519.PrivateKey
publicKeyCrossLogin ed25519.PublicKey // For accepting externally generated JWTs publicKeyCrossLogin ed25519.PublicKey // For accepting externally generated JWTs
@ -28,9 +26,7 @@ type JWTCookieSessionAuthenticator struct {
var _ Authenticator = (*JWTCookieSessionAuthenticator)(nil) var _ Authenticator = (*JWTCookieSessionAuthenticator)(nil)
func (ja *JWTCookieSessionAuthenticator) Init(auth *Authentication, conf interface{}) error { func (ja *JWTCookieSessionAuthenticator) Init(conf interface{}) error {
ja.auth = auth
ja.config = conf.(*schema.JWTAuthConfig) ja.config = conf.(*schema.JWTAuthConfig)
pubKey, privKey := os.Getenv("JWT_PUBLIC_KEY"), os.Getenv("JWT_PRIVATE_KEY") pubKey, privKey := os.Getenv("JWT_PUBLIC_KEY"), os.Getenv("JWT_PRIVATE_KEY")

View File

@ -11,6 +11,7 @@ import (
"os" "os"
"strings" "strings"
"github.com/ClusterCockpit/cc-backend/internal/repository"
"github.com/ClusterCockpit/cc-backend/pkg/log" "github.com/ClusterCockpit/cc-backend/pkg/log"
"github.com/ClusterCockpit/cc-backend/pkg/schema" "github.com/ClusterCockpit/cc-backend/pkg/schema"
"github.com/golang-jwt/jwt/v4" "github.com/golang-jwt/jwt/v4"
@ -18,11 +19,15 @@ import (
type JWTSessionAuthenticator struct { type JWTSessionAuthenticator struct {
loginTokenKey []byte // HS256 key loginTokenKey []byte // HS256 key
config *schema.JWTAuthConfig
} }
var _ Authenticator = (*JWTSessionAuthenticator)(nil) var _ Authenticator = (*JWTSessionAuthenticator)(nil)
func (ja *JWTSessionAuthenticator) Init(auth *Authentication, conf interface{}) error { func (ja *JWTSessionAuthenticator) Init(conf interface{}) error {
ja.config = conf.(*schema.JWTAuthConfig)
if pubKey := os.Getenv("CROSS_LOGIN_JWT_HS512_KEY"); pubKey != "" { if pubKey := os.Getenv("CROSS_LOGIN_JWT_HS512_KEY"); pubKey != "" {
bytes, err := base64.StdEncoding.DecodeString(pubKey) bytes, err := base64.StdEncoding.DecodeString(pubKey)
if err != nil { if err != nil {
@ -124,6 +129,10 @@ func (ja *JWTSessionAuthenticator) Login(
AuthType: schema.AuthSession, AuthType: schema.AuthSession,
AuthSource: schema.AuthViaToken, AuthSource: schema.AuthViaToken,
} }
if err := repository.GetUserRepository().AddUser(user); err != nil {
log.Errorf("Error while adding user '%s' to DB", user.Username)
}
} }
return user, nil return user, nil

View File

@ -12,24 +12,21 @@ import (
"strings" "strings"
"time" "time"
"github.com/ClusterCockpit/cc-backend/internal/repository"
"github.com/ClusterCockpit/cc-backend/pkg/log" "github.com/ClusterCockpit/cc-backend/pkg/log"
"github.com/ClusterCockpit/cc-backend/pkg/schema" "github.com/ClusterCockpit/cc-backend/pkg/schema"
"github.com/go-ldap/ldap/v3" "github.com/go-ldap/ldap/v3"
) )
type LdapAuthenticator struct { type LdapAuthenticator struct {
auth *Authentication
config *schema.LdapConfig config *schema.LdapConfig
syncPassword string syncPassword string
} }
var _ Authenticator = (*LdapAuthenticator)(nil) var _ Authenticator = (*LdapAuthenticator)(nil)
func (la *LdapAuthenticator) Init( func (la *LdapAuthenticator) Init(conf interface{}) error {
auth *Authentication,
conf interface{}) error {
la.auth = auth
la.config = conf.(*schema.LdapConfig) la.config = conf.(*schema.LdapConfig)
la.syncPassword = os.Getenv("LDAP_ADMIN_PASSWORD") la.syncPassword = os.Getenv("LDAP_ADMIN_PASSWORD")
@ -101,13 +98,30 @@ func (la *LdapAuthenticator) CanLogin(
entry := sr.Entries[0] entry := sr.Entries[0]
name := entry.GetAttributeValue("gecos") name := entry.GetAttributeValue("gecos")
var roles []string
roles = append(roles, schema.GetRoleString(schema.RoleUser))
projects := make([]string, 0)
if _, err := la.auth.db.Exec(`INSERT INTO user (username, ldap, name, roles) VALUES (?, ?, ?, ?)`, user = &schema.User{
username, 1, name, "[\""+schema.GetRoleString(schema.RoleUser)+"\"]"); err != nil { Username: username,
log.Errorf("User '%s' new in LDAP: Insert into DB failed", username) Name: name,
Roles: roles,
Projects: projects,
AuthType: schema.AuthSession,
AuthSource: schema.AuthViaLDAP,
}
if err := repository.GetUserRepository().AddUser(user); err != nil {
log.Errorf("User '%s' LDAP: Insert into DB failed", username)
return false return false
} }
// if _, err := la.auth.db.Exec(`INSERT INTO user (username, ldap, name, roles) VALUES (?, ?, ?, ?)`,
// username, 1, name, "[\""+schema.GetRoleString(schema.RoleUser)+"\"]"); err != nil {
// log.Errorf("User '%s' new in LDAP: Insert into DB failed", username)
// return false
// }
return true return true
} }
} }
@ -137,25 +151,18 @@ func (la *LdapAuthenticator) Login(
} }
func (la *LdapAuthenticator) Sync() error { func (la *LdapAuthenticator) Sync() error {
const IN_DB int = 1 const IN_DB int = 1
const IN_LDAP int = 2 const IN_LDAP int = 2
const IN_BOTH int = 3 const IN_BOTH int = 3
ur := repository.GetUserRepository()
users := map[string]int{} users := map[string]int{}
rows, err := la.auth.db.Query(`SELECT username FROM user WHERE user.ldap = 1`) usernames, err := ur.GetLdapUsernames()
if err != nil { if err != nil {
log.Warn("Error while querying LDAP users")
return err return err
} }
for rows.Next() { for _, username := range usernames {
var username string
if err := rows.Scan(&username); err != nil {
log.Warnf("Error while scanning for user '%s'", username)
return err
}
users[username] = IN_DB users[username] = IN_DB
} }
@ -194,17 +201,26 @@ func (la *LdapAuthenticator) Sync() error {
for username, where := range users { for username, where := range users {
if where == IN_DB && la.config.SyncDelOldUsers { if where == IN_DB && la.config.SyncDelOldUsers {
ur.DelUser(username)
log.Debugf("sync: remove %v (does not show up in LDAP anymore)", username) log.Debugf("sync: remove %v (does not show up in LDAP anymore)", username)
if _, err := la.auth.db.Exec(`DELETE FROM user WHERE user.username = ?`, username); err != nil {
log.Errorf("User '%s' not in LDAP anymore: Delete from DB failed", username)
return err
}
} else if where == IN_LDAP { } else if where == IN_LDAP {
name := newnames[username] name := newnames[username]
var roles []string
roles = append(roles, schema.GetRoleString(schema.RoleUser))
projects := make([]string, 0)
user := &schema.User{
Username: username,
Name: name,
Roles: roles,
Projects: projects,
AuthSource: schema.AuthViaLDAP,
}
log.Debugf("sync: add %v (name: %v, roles: [user], ldap: true)", username, name) log.Debugf("sync: add %v (name: %v, roles: [user], ldap: true)", username, name)
if _, err := la.auth.db.Exec(`INSERT INTO user (username, ldap, name, roles) VALUES (?, ?, ?, ?)`, if err := ur.AddUser(user); err != nil {
username, 1, name, "[\""+schema.GetRoleString(schema.RoleUser)+"\"]"); err != nil { log.Errorf("User '%s' LDAP: Insert into DB failed", username)
log.Errorf("User '%s' new in LDAP: Insert into DB failed", username)
return err return err
} }
} }

View File

@ -20,10 +20,8 @@ type LocalAuthenticator struct {
var _ Authenticator = (*LocalAuthenticator)(nil) var _ Authenticator = (*LocalAuthenticator)(nil)
func (la *LocalAuthenticator) Init( func (la *LocalAuthenticator) Init(
auth *Authentication,
_ interface{}) error { _ interface{}) error {
la.auth = auth
return nil return nil
} }

View File

@ -71,6 +71,28 @@ func (r *UserRepository) GetUser(username string) (*schema.User, error) {
return user, nil return user, nil
} }
func (r *UserRepository) GetLdapUsernames() ([]string, error) {
var users []string
rows, err := r.DB.Query(`SELECT username FROM user WHERE user.ldap = 1`)
if err != nil {
log.Warn("Error while querying usernames")
return nil, err
}
for rows.Next() {
var username string
if err := rows.Scan(&username); err != nil {
log.Warnf("Error while scanning for user '%s'", username)
return nil, err
}
users = append(users, username)
}
return users, nil
}
func (r *UserRepository) AddUser(user *schema.User) error { func (r *UserRepository) AddUser(user *schema.User) error {
rolesJson, _ := json.Marshal(user.Roles) rolesJson, _ := json.Marshal(user.Roles)
projectsJson, _ := json.Marshal(user.Projects) projectsJson, _ := json.Marshal(user.Projects)
@ -95,6 +117,10 @@ func (r *UserRepository) AddUser(user *schema.User) error {
cols = append(cols, "password") cols = append(cols, "password")
vals = append(vals, string(password)) vals = append(vals, string(password))
} }
if user.AuthSource != -1 {
cols = append(cols, "ldap")
vals = append(vals, int(user.AuthSource))
}
if _, err := sq.Insert("user").Columns(cols...).Values(vals...).RunWith(r.DB).Exec(); err != nil { if _, err := sq.Insert("user").Columns(cols...).Values(vals...).RunWith(r.DB).Exec(); err != nil {
log.Errorf("Error while inserting new user '%v' into DB", user.Username) log.Errorf("Error while inserting new user '%v' into DB", user.Username)

View File

@ -27,6 +27,7 @@ const (
AuthViaLocalPassword AuthSource = iota AuthViaLocalPassword AuthSource = iota
AuthViaLDAP AuthViaLDAP
AuthViaToken AuthViaToken
AuthViaAll
) )
type AuthType int type AuthType int