From 15231bc68358719deb48b4583999744ea38dc92d Mon Sep 17 00:00:00 2001 From: Jan Eitzinger Date: Thu, 17 Aug 2023 12:34:30 +0200 Subject: [PATCH] Cleanup and adapt to new structure --- cmd/cc-backend/main.go | 2 +- internal/auth/auth.go | 33 ++++------------ internal/auth/jwt.go | 2 +- internal/auth/jwtCookieSession.go | 6 +-- internal/auth/jwtSession.go | 11 +++++- internal/auth/ldap.go | 66 +++++++++++++++++++------------ internal/auth/local.go | 2 - internal/repository/user.go | 26 ++++++++++++ pkg/schema/user.go | 1 + 9 files changed, 89 insertions(+), 60 deletions(-) diff --git a/cmd/cc-backend/main.go b/cmd/cc-backend/main.go index 60c5edd..dd00046 100644 --- a/cmd/cc-backend/main.go +++ b/cmd/cc-backend/main.go @@ -211,7 +211,7 @@ func main() { var authentication *auth.Authentication if !config.Keys.DisableAuthentication { 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, "jwt": config.Keys.JwtConfig, }); err != nil { diff --git a/internal/auth/auth.go b/internal/auth/auth.go index d79f28b..cc2bcb4 100644 --- a/internal/auth/auth.go +++ b/internal/auth/auth.go @@ -18,17 +18,15 @@ import ( "github.com/ClusterCockpit/cc-backend/pkg/log" "github.com/ClusterCockpit/cc-backend/pkg/schema" "github.com/gorilla/sessions" - "github.com/jmoiron/sqlx" ) 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 Login(user *schema.User, rw http.ResponseWriter, r *http.Request) (*schema.User, error) } type Authentication struct { - db *sqlx.DB sessionStore *sessions.CookieStore SessionMaxAge time.Duration @@ -82,10 +80,8 @@ func (auth *Authentication) AuthViaSession( }, nil } -func Init(db *sqlx.DB, - configs map[string]interface{}) (*Authentication, error) { +func Init(configs map[string]interface{}) (*Authentication, error) { auth := &Authentication{} - auth.db = db sessKey := os.Getenv("SESSION_KEY") if sessKey == "" { @@ -106,14 +102,14 @@ func Init(db *sqlx.DB, } 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") return nil, err } if config, ok := configs["ldap"]; ok { 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") } else { auth.LdapAuth = ldapAuth @@ -122,21 +118,21 @@ func Init(db *sqlx.DB, } 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") } else { auth.authenticators = append(auth.authenticators, jwtSessionAuth) } 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") } else { auth.authenticators = append(auth.authenticators, jwtCookieSessionAuth) } 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") return nil, err } @@ -150,13 +146,12 @@ func (auth *Authentication) Login( onfailure func(rw http.ResponseWriter, r *http.Request, loginErr error)) http.Handler { return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { - ur := repository.GetUserRepository() err := errors.New("no authenticator applied") username := r.FormValue("username") dbUser := (*schema.User)(nil) if username != "" { - dbUser, err = ur.GetUser(username) + dbUser, err = repository.GetUserRepository().GetUser(username) if err != nil && err != sql.ErrNoRows { log.Errorf("Error while loading user '%v'", username) } @@ -166,10 +161,6 @@ func (auth *Authentication) Login( if !authenticator.CanLogin(dbUser, username, rw, r) { 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) if err != nil { @@ -197,14 +188,6 @@ func (auth *Authentication) Login( 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) ctx := context.WithValue(r.Context(), repository.ContextUserKey, user) onsuccess.ServeHTTP(rw, r.WithContext(ctx)) diff --git a/internal/auth/jwt.go b/internal/auth/jwt.go index 0690b9b..9c0166d 100644 --- a/internal/auth/jwt.go +++ b/internal/auth/jwt.go @@ -25,7 +25,7 @@ type JWTAuthenticator struct { config *schema.JWTAuthConfig } -func (ja *JWTAuthenticator) Init(auth *Authentication, conf interface{}) error { +func (ja *JWTAuthenticator) Init(conf interface{}) error { ja.config = conf.(*schema.JWTAuthConfig) pubKey, privKey := os.Getenv("JWT_PUBLIC_KEY"), os.Getenv("JWT_PRIVATE_KEY") diff --git a/internal/auth/jwtCookieSession.go b/internal/auth/jwtCookieSession.go index 4787107..ad0b033 100644 --- a/internal/auth/jwtCookieSession.go +++ b/internal/auth/jwtCookieSession.go @@ -17,8 +17,6 @@ import ( ) type JWTCookieSessionAuthenticator struct { - auth *Authentication - publicKey ed25519.PublicKey privateKey ed25519.PrivateKey publicKeyCrossLogin ed25519.PublicKey // For accepting externally generated JWTs @@ -28,9 +26,7 @@ type JWTCookieSessionAuthenticator struct { var _ Authenticator = (*JWTCookieSessionAuthenticator)(nil) -func (ja *JWTCookieSessionAuthenticator) Init(auth *Authentication, conf interface{}) error { - - ja.auth = auth +func (ja *JWTCookieSessionAuthenticator) Init(conf interface{}) error { ja.config = conf.(*schema.JWTAuthConfig) pubKey, privKey := os.Getenv("JWT_PUBLIC_KEY"), os.Getenv("JWT_PRIVATE_KEY") diff --git a/internal/auth/jwtSession.go b/internal/auth/jwtSession.go index 59250ac..5a29360 100644 --- a/internal/auth/jwtSession.go +++ b/internal/auth/jwtSession.go @@ -11,6 +11,7 @@ import ( "os" "strings" + "github.com/ClusterCockpit/cc-backend/internal/repository" "github.com/ClusterCockpit/cc-backend/pkg/log" "github.com/ClusterCockpit/cc-backend/pkg/schema" "github.com/golang-jwt/jwt/v4" @@ -18,11 +19,15 @@ import ( type JWTSessionAuthenticator struct { loginTokenKey []byte // HS256 key + + config *schema.JWTAuthConfig } 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 != "" { bytes, err := base64.StdEncoding.DecodeString(pubKey) if err != nil { @@ -124,6 +129,10 @@ func (ja *JWTSessionAuthenticator) Login( AuthType: schema.AuthSession, 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 diff --git a/internal/auth/ldap.go b/internal/auth/ldap.go index 925e967..c3bde4a 100644 --- a/internal/auth/ldap.go +++ b/internal/auth/ldap.go @@ -12,24 +12,21 @@ import ( "strings" "time" + "github.com/ClusterCockpit/cc-backend/internal/repository" "github.com/ClusterCockpit/cc-backend/pkg/log" "github.com/ClusterCockpit/cc-backend/pkg/schema" "github.com/go-ldap/ldap/v3" ) type LdapAuthenticator struct { - auth *Authentication config *schema.LdapConfig syncPassword string } var _ Authenticator = (*LdapAuthenticator)(nil) -func (la *LdapAuthenticator) Init( - auth *Authentication, - conf interface{}) error { +func (la *LdapAuthenticator) Init(conf interface{}) error { - la.auth = auth la.config = conf.(*schema.LdapConfig) la.syncPassword = os.Getenv("LDAP_ADMIN_PASSWORD") @@ -101,13 +98,30 @@ func (la *LdapAuthenticator) CanLogin( entry := sr.Entries[0] 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 (?, ?, ?, ?)`, - username, 1, name, "[\""+schema.GetRoleString(schema.RoleUser)+"\"]"); err != nil { - log.Errorf("User '%s' new in LDAP: Insert into DB failed", username) + user = &schema.User{ + Username: 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 } + // 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 } } @@ -137,25 +151,18 @@ func (la *LdapAuthenticator) Login( } func (la *LdapAuthenticator) Sync() error { - const IN_DB int = 1 const IN_LDAP int = 2 const IN_BOTH int = 3 + ur := repository.GetUserRepository() 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 { - log.Warn("Error while querying LDAP users") return err } - for rows.Next() { - var username string - if err := rows.Scan(&username); err != nil { - log.Warnf("Error while scanning for user '%s'", username) - return err - } - + for _, username := range usernames { users[username] = IN_DB } @@ -194,17 +201,26 @@ func (la *LdapAuthenticator) Sync() error { for username, where := range users { if where == IN_DB && la.config.SyncDelOldUsers { + ur.DelUser(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 { 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) - 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) + if err := ur.AddUser(user); err != nil { + log.Errorf("User '%s' LDAP: Insert into DB failed", username) return err } } diff --git a/internal/auth/local.go b/internal/auth/local.go index fb1ba0f..0c6303a 100644 --- a/internal/auth/local.go +++ b/internal/auth/local.go @@ -20,10 +20,8 @@ type LocalAuthenticator struct { var _ Authenticator = (*LocalAuthenticator)(nil) func (la *LocalAuthenticator) Init( - auth *Authentication, _ interface{}) error { - la.auth = auth return nil } diff --git a/internal/repository/user.go b/internal/repository/user.go index 3db7e4d..5439b6e 100644 --- a/internal/repository/user.go +++ b/internal/repository/user.go @@ -71,6 +71,28 @@ func (r *UserRepository) GetUser(username string) (*schema.User, error) { 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 { rolesJson, _ := json.Marshal(user.Roles) projectsJson, _ := json.Marshal(user.Projects) @@ -95,6 +117,10 @@ func (r *UserRepository) AddUser(user *schema.User) error { cols = append(cols, "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 { log.Errorf("Error while inserting new user '%v' into DB", user.Username) diff --git a/pkg/schema/user.go b/pkg/schema/user.go index 1547d3f..047f617 100644 --- a/pkg/schema/user.go +++ b/pkg/schema/user.go @@ -27,6 +27,7 @@ const ( AuthViaLocalPassword AuthSource = iota AuthViaLDAP AuthViaToken + AuthViaAll ) type AuthType int