Merge branch 'master' into dev

This commit is contained in:
Jan Eitzinger 2025-02-28 09:17:39 +01:00
commit c10737bfd7
4 changed files with 112 additions and 3 deletions

View File

@ -0,0 +1,12 @@
{
"clusters": [
{
"name": "fritz",
"default_metrics": "cpu_load, flops_any, core_power, lustre_open, mem_used, mem_bw, net_bytes_in"
},
{
"name": "alex",
"default_metrics": "flops_any, mem_bw, mem_used, vectorization_ratio"
}
]
}

View File

@ -10,11 +10,14 @@ import (
"database/sql" "database/sql"
"encoding/base64" "encoding/base64"
"errors" "errors"
"net"
"net/http" "net/http"
"os" "os"
"sync" "sync"
"time" "time"
"golang.org/x/time/rate"
"github.com/ClusterCockpit/cc-backend/internal/config" "github.com/ClusterCockpit/cc-backend/internal/config"
"github.com/ClusterCockpit/cc-backend/internal/repository" "github.com/ClusterCockpit/cc-backend/internal/repository"
"github.com/ClusterCockpit/cc-backend/pkg/log" "github.com/ClusterCockpit/cc-backend/pkg/log"
@ -32,6 +35,19 @@ var (
authInstance *Authentication authInstance *Authentication
) )
var ipUserLimiters sync.Map
func getIPUserLimiter(ip, username string) *rate.Limiter {
key := ip + ":" + username
limiter, ok := ipUserLimiters.Load(key)
if !ok {
newLimiter := rate.NewLimiter(rate.Every(time.Hour/10), 10)
ipUserLimiters.Store(key, newLimiter)
return newLimiter
}
return limiter.(*rate.Limiter)
}
type Authentication struct { type Authentication struct {
sessionStore *sessions.CookieStore sessionStore *sessions.CookieStore
LdapAuth *LdapAuthenticator LdapAuth *LdapAuthenticator
@ -88,7 +104,7 @@ func Init() {
authInstance.sessionStore = sessions.NewCookieStore(bytes) authInstance.sessionStore = sessions.NewCookieStore(bytes)
} }
if d, err := time.ParseDuration(config.Keys.SessionMaxAge); err != nil { if d, err := time.ParseDuration(config.Keys.SessionMaxAge); err == nil {
authInstance.SessionMaxAge = d authInstance.SessionMaxAge = d
} }
@ -208,9 +224,21 @@ func (auth *Authentication) Login(
onfailure func(rw http.ResponseWriter, r *http.Request, loginErr error), onfailure func(rw http.ResponseWriter, r *http.Request, loginErr error),
) http.Handler { ) http.Handler {
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
username := r.FormValue("username") ip, _, err := net.SplitHostPort(r.RemoteAddr)
var dbUser *schema.User if err != nil {
ip = r.RemoteAddr
}
username := r.FormValue("username")
limiter := getIPUserLimiter(ip, username)
if !limiter.Allow() {
log.Warnf("AUTH/RATE > Too many login attempts for combination IP: %s, Username: %s", ip, username)
onfailure(rw, r, errors.New("Too many login attempts, try again in a few minutes."))
return
}
var dbUser *schema.User
if username != "" { if username != "" {
var err error var err error
dbUser, err = repository.GetUserRepository().GetUser(username) dbUser, err = repository.GetUserRepository().GetUser(username)

View File

@ -0,0 +1,44 @@
package config
import (
"encoding/json"
"os"
"strings"
)
type DefaultMetricsCluster struct {
Name string `json:"name"`
DefaultMetrics string `json:"default_metrics"`
}
type DefaultMetricsConfig struct {
Clusters []DefaultMetricsCluster `json:"clusters"`
}
func LoadDefaultMetricsConfig() (*DefaultMetricsConfig, error) {
filePath := "configs/default_metrics.json"
if _, err := os.Stat(filePath); os.IsNotExist(err) {
return nil, nil
}
data, err := os.ReadFile(filePath)
if err != nil {
return nil, err
}
var cfg DefaultMetricsConfig
if err := json.Unmarshal(data, &cfg); err != nil {
return nil, err
}
return &cfg, nil
}
func ParseMetricsString(s string) []string {
parts := strings.Split(s, ",")
var metrics []string
for _, p := range parts {
trimmed := strings.TrimSpace(p)
if trimmed != "" {
metrics = append(metrics, trimmed)
}
}
return metrics
}

View File

@ -19,6 +19,7 @@ import (
sq "github.com/Masterminds/squirrel" sq "github.com/Masterminds/squirrel"
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
"golang.org/x/crypto/bcrypt" "golang.org/x/crypto/bcrypt"
"github.com/ClusterCockpit/cc-backend/internal/config"
) )
var ( var (
@ -127,6 +128,30 @@ func (r *UserRepository) AddUser(user *schema.User) error {
} }
log.Infof("new user %#v created (roles: %s, auth-source: %d, projects: %s)", user.Username, rolesJson, user.AuthSource, projectsJson) log.Infof("new user %#v created (roles: %s, auth-source: %d, projects: %s)", user.Username, rolesJson, user.AuthSource, projectsJson)
defaultMetricsCfg, err := config.LoadDefaultMetricsConfig()
if err != nil {
log.Errorf("Error loading default metrics config: %v", err)
} else if defaultMetricsCfg != nil {
for _, cluster := range defaultMetricsCfg.Clusters {
metricsArray := config.ParseMetricsString(cluster.DefaultMetrics)
metricsJSON, err := json.Marshal(metricsArray)
if err != nil {
log.Errorf("Error marshaling default metrics for cluster %s: %v", cluster.Name, err)
continue
}
confKey := "job_view_selectedMetrics:" + cluster.Name
if _, err := sq.Insert("configuration").
Columns("username", "confkey", "value").
Values(user.Username, confKey, string(metricsJSON)).
RunWith(r.DB).Exec(); err != nil {
log.Errorf("Error inserting default job view metrics for user %s and cluster %s: %v", user.Username, cluster.Name, err)
} else {
log.Infof("Default job view metrics for user %s and cluster %s set to %s", user.Username, cluster.Name, string(metricsJSON))
}
}
}
return nil return nil
} }