mirror of
				https://github.com/ClusterCockpit/cc-backend
				synced 2025-11-04 09:35:07 +01:00 
			
		
		
		
	authentication: roles as regular array; simplified LDAP
This commit is contained in:
		
							
								
								
									
										11
									
								
								api/rest.go
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								api/rest.go
									
									
									
									
									
								
							@@ -12,6 +12,7 @@ import (
 | 
				
			|||||||
	"path/filepath"
 | 
						"path/filepath"
 | 
				
			||||||
	"sync"
 | 
						"sync"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/ClusterCockpit/cc-jobarchive/auth"
 | 
				
			||||||
	"github.com/ClusterCockpit/cc-jobarchive/config"
 | 
						"github.com/ClusterCockpit/cc-jobarchive/config"
 | 
				
			||||||
	"github.com/ClusterCockpit/cc-jobarchive/graph"
 | 
						"github.com/ClusterCockpit/cc-jobarchive/graph"
 | 
				
			||||||
	"github.com/ClusterCockpit/cc-jobarchive/graph/model"
 | 
						"github.com/ClusterCockpit/cc-jobarchive/graph/model"
 | 
				
			||||||
@@ -177,6 +178,11 @@ func (api *RestApi) tagJob(rw http.ResponseWriter, r *http.Request) {
 | 
				
			|||||||
// A new job started. The body should be in the `meta.json` format, but some fields required
 | 
					// A new job started. The body should be in the `meta.json` format, but some fields required
 | 
				
			||||||
// there are optional here (e.g. `jobState` defaults to "running").
 | 
					// there are optional here (e.g. `jobState` defaults to "running").
 | 
				
			||||||
func (api *RestApi) startJob(rw http.ResponseWriter, r *http.Request) {
 | 
					func (api *RestApi) startJob(rw http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
 | 
						if user := auth.GetUser(r.Context()); user != nil && !user.HasRole(auth.RoleApi) {
 | 
				
			||||||
 | 
							http.Error(rw, "Missing 'api' role", http.StatusForbidden)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	req := schema.JobMeta{BaseJob: schema.JobDefaults}
 | 
						req := schema.JobMeta{BaseJob: schema.JobDefaults}
 | 
				
			||||||
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
 | 
						if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
 | 
				
			||||||
		http.Error(rw, err.Error(), http.StatusBadRequest)
 | 
							http.Error(rw, err.Error(), http.StatusBadRequest)
 | 
				
			||||||
@@ -246,6 +252,11 @@ func (api *RestApi) startJob(rw http.ResponseWriter, r *http.Request) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// A job has stopped and should be archived.
 | 
					// A job has stopped and should be archived.
 | 
				
			||||||
func (api *RestApi) stopJob(rw http.ResponseWriter, r *http.Request) {
 | 
					func (api *RestApi) stopJob(rw http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
 | 
						if user := auth.GetUser(r.Context()); user != nil && !user.HasRole(auth.RoleApi) {
 | 
				
			||||||
 | 
							http.Error(rw, "Missing 'api' role", http.StatusForbidden)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	req := StopJobApiRequest{}
 | 
						req := StopJobApiRequest{}
 | 
				
			||||||
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
 | 
						if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
 | 
				
			||||||
		http.Error(rw, err.Error(), http.StatusBadRequest)
 | 
							http.Error(rw, err.Error(), http.StatusBadRequest)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										78
									
								
								auth/auth.go
									
									
									
									
									
								
							
							
						
						
									
										78
									
								
								auth/auth.go
									
									
									
									
									
								
							@@ -28,12 +28,26 @@ type User struct {
 | 
				
			|||||||
	Username string
 | 
						Username string
 | 
				
			||||||
	Password string
 | 
						Password string
 | 
				
			||||||
	Name     string
 | 
						Name     string
 | 
				
			||||||
	IsAdmin   bool
 | 
						Roles    []string
 | 
				
			||||||
	IsAPIUser bool
 | 
					 | 
				
			||||||
	ViaLdap  bool
 | 
						ViaLdap  bool
 | 
				
			||||||
	Email    string
 | 
						Email    string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						RoleAdmin string = "admin"
 | 
				
			||||||
 | 
						RoleApi   string = "api"
 | 
				
			||||||
 | 
						RoleUser  string = "user"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (u *User) HasRole(role string) bool {
 | 
				
			||||||
 | 
						for _, r := range u.Roles {
 | 
				
			||||||
 | 
							if r == role {
 | 
				
			||||||
 | 
								return true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type ContextKey string
 | 
					type ContextKey string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const ContextUserKey ContextKey = "user"
 | 
					const ContextUserKey ContextKey = "user"
 | 
				
			||||||
@@ -100,24 +114,32 @@ func Init(db *sqlx.DB, ldapConfig *LdapConfig) error {
 | 
				
			|||||||
// arg must be formated like this: "<username>:[admin]:<password>"
 | 
					// arg must be formated like this: "<username>:[admin]:<password>"
 | 
				
			||||||
func AddUserToDB(db *sqlx.DB, arg string) error {
 | 
					func AddUserToDB(db *sqlx.DB, arg string) error {
 | 
				
			||||||
	parts := strings.SplitN(arg, ":", 3)
 | 
						parts := strings.SplitN(arg, ":", 3)
 | 
				
			||||||
	if len(parts) != 3 || len(parts[0]) == 0 || len(parts[2]) == 0 || !(len(parts[1]) == 0 || parts[1] == "admin") {
 | 
						if len(parts) != 3 || len(parts[0]) == 0 {
 | 
				
			||||||
		return errors.New("invalid argument format")
 | 
							return errors.New("invalid argument format")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	password, err := bcrypt.GenerateFromPassword([]byte(parts[2]), bcrypt.DefaultCost)
 | 
						password := ""
 | 
				
			||||||
 | 
						if len(parts[2]) > 0 {
 | 
				
			||||||
 | 
							bytes, err := bcrypt.GenerateFromPassword([]byte(parts[2]), bcrypt.DefaultCost)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							password = string(bytes)
 | 
				
			||||||
	roles := "[]"
 | 
					 | 
				
			||||||
	if parts[1] == "admin" {
 | 
					 | 
				
			||||||
		roles = "[\"ROLE_ADMIN\"]"
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if parts[1] == "api" {
 | 
					 | 
				
			||||||
		roles = "[\"ROLE_API\"]"
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_, err = sq.Insert("user").Columns("username", "password", "roles").Values(parts[0], string(password), roles).RunWith(db).Exec()
 | 
						roles := []string{}
 | 
				
			||||||
 | 
						for _, role := range strings.Split(parts[1], ",") {
 | 
				
			||||||
 | 
							if len(role) == 0 {
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							} else if role == RoleAdmin || role == RoleApi || role == RoleUser {
 | 
				
			||||||
 | 
								roles = append(roles, role)
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								return fmt.Errorf("invalid user role: %#v", role)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rolesJson, _ := json.Marshal(roles)
 | 
				
			||||||
 | 
						_, err := sq.Insert("user").Columns("username", "password", "roles").Values(parts[0], password, string(rolesJson)).RunWith(db).Exec()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -142,17 +164,8 @@ func FetchUserFromDB(db *sqlx.DB, username string) (*User, error) {
 | 
				
			|||||||
	user.Password = hashedPassword.String
 | 
						user.Password = hashedPassword.String
 | 
				
			||||||
	user.Name = name.String
 | 
						user.Name = name.String
 | 
				
			||||||
	user.Email = email.String
 | 
						user.Email = email.String
 | 
				
			||||||
	var roles []string
 | 
					 | 
				
			||||||
	if rawRoles.Valid {
 | 
						if rawRoles.Valid {
 | 
				
			||||||
		json.Unmarshal([]byte(rawRoles.String), &roles)
 | 
							json.Unmarshal([]byte(rawRoles.String), &user.Roles)
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	for _, role := range roles {
 | 
					 | 
				
			||||||
		switch role {
 | 
					 | 
				
			||||||
		case "ROLE_ADMIN":
 | 
					 | 
				
			||||||
			user.IsAdmin = true
 | 
					 | 
				
			||||||
		case "ROLE_API":
 | 
					 | 
				
			||||||
			user.IsAPIUser = true
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return user, nil
 | 
						return user, nil
 | 
				
			||||||
@@ -195,14 +208,14 @@ func Login(db *sqlx.DB) http.Handler {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		session.Options.MaxAge = 30 * 24 * 60 * 60
 | 
							session.Options.MaxAge = 30 * 24 * 60 * 60
 | 
				
			||||||
		session.Values["username"] = user.Username
 | 
							session.Values["username"] = user.Username
 | 
				
			||||||
		session.Values["is_admin"] = user.IsAdmin
 | 
							session.Values["roles"] = user.Roles
 | 
				
			||||||
		if err := sessionStore.Save(r, rw, session); err != nil {
 | 
							if err := sessionStore.Save(r, rw, session); err != nil {
 | 
				
			||||||
			log.Printf("session save failed: %s\n", err.Error())
 | 
								log.Printf("session save failed: %s\n", err.Error())
 | 
				
			||||||
			http.Error(rw, err.Error(), http.StatusInternalServerError)
 | 
								http.Error(rw, err.Error(), http.StatusInternalServerError)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		log.Printf("login successfull: user: %#v\n", user)
 | 
							log.Printf("login successfull: user: %#v (roles: %v)\n", user.Username, user.Roles)
 | 
				
			||||||
		http.Redirect(rw, r, "/", http.StatusTemporaryRedirect)
 | 
							http.Redirect(rw, r, "/", http.StatusTemporaryRedirect)
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -240,14 +253,12 @@ func authViaToken(r *http.Request) (*User, error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	claims := token.Claims.(jwt.MapClaims)
 | 
						claims := token.Claims.(jwt.MapClaims)
 | 
				
			||||||
	sub, _ := claims["sub"].(string)
 | 
						sub, _ := claims["sub"].(string)
 | 
				
			||||||
	isAdmin, _ := claims["is_admin"].(bool)
 | 
						roles, _ := claims["roles"].([]string)
 | 
				
			||||||
	isAPIUser, _ := claims["is_api"].(bool)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// TODO: Check if sub is still a valid user!
 | 
						// TODO: Check if sub is still a valid user!
 | 
				
			||||||
	return &User{
 | 
						return &User{
 | 
				
			||||||
		Username: sub,
 | 
							Username: sub,
 | 
				
			||||||
		IsAdmin:   isAdmin,
 | 
							Roles:    roles,
 | 
				
			||||||
		IsAPIUser: isAPIUser,
 | 
					 | 
				
			||||||
	}, nil
 | 
						}, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -289,9 +300,11 @@ func Auth(next http.Handler) http.Handler {
 | 
				
			|||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							username, _ := session.Values["username"].(string)
 | 
				
			||||||
 | 
							roles, _ := session.Values["roles"].([]string)
 | 
				
			||||||
		ctx := context.WithValue(r.Context(), ContextUserKey, &User{
 | 
							ctx := context.WithValue(r.Context(), ContextUserKey, &User{
 | 
				
			||||||
			Username: session.Values["username"].(string),
 | 
								Username: username,
 | 
				
			||||||
			IsAdmin:  session.Values["is_admin"].(bool),
 | 
								Roles:    roles,
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
		next.ServeHTTP(rw, r.WithContext(ctx))
 | 
							next.ServeHTTP(rw, r.WithContext(ctx))
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
@@ -300,13 +313,12 @@ func Auth(next http.Handler) http.Handler {
 | 
				
			|||||||
// Generate a new JWT that can be used for authentication
 | 
					// Generate a new JWT that can be used for authentication
 | 
				
			||||||
func ProvideJWT(user *User) (string, error) {
 | 
					func ProvideJWT(user *User) (string, error) {
 | 
				
			||||||
	if JwtPrivateKey == nil {
 | 
						if JwtPrivateKey == nil {
 | 
				
			||||||
		return "", errors.New("environment variable 'JWT_PUBLIC_KEY' not set")
 | 
							return "", errors.New("environment variable 'JWT_PRIVATE_KEY' not set")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tok := jwt.NewWithClaims(jwt.SigningMethodEdDSA, jwt.MapClaims{
 | 
						tok := jwt.NewWithClaims(jwt.SigningMethodEdDSA, jwt.MapClaims{
 | 
				
			||||||
		"sub":   user.Username,
 | 
							"sub":   user.Username,
 | 
				
			||||||
		"is_admin": user.IsAdmin,
 | 
							"roles": user.Roles,
 | 
				
			||||||
		"is_api":   user.IsAPIUser,
 | 
					 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return tok.SignedString(JwtPrivateKey)
 | 
						return tok.SignedString(JwtPrivateKey)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										37
									
								
								auth/ldap.go
									
									
									
									
									
								
							
							
						
						
									
										37
									
								
								auth/ldap.go
									
									
									
									
									
								
							@@ -7,7 +7,6 @@ import (
 | 
				
			|||||||
	"log"
 | 
						"log"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"sync"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/go-ldap/ldap/v3"
 | 
						"github.com/go-ldap/ldap/v3"
 | 
				
			||||||
	"github.com/jmoiron/sqlx"
 | 
						"github.com/jmoiron/sqlx"
 | 
				
			||||||
@@ -37,22 +36,9 @@ func initLdap(config *LdapConfig) error {
 | 
				
			|||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var ldapConnectionsLock sync.Mutex
 | 
					 | 
				
			||||||
var ldapConnections []*ldap.Conn = []*ldap.Conn{}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// TODO: Add a connection pool or something like
 | 
					// TODO: Add a connection pool or something like
 | 
				
			||||||
// that so that connections can be reused/cached.
 | 
					// that so that connections can be reused/cached.
 | 
				
			||||||
func getLdapConnection() (*ldap.Conn, error) {
 | 
					func getLdapConnection(admin bool) (*ldap.Conn, error) {
 | 
				
			||||||
	ldapConnectionsLock.Lock()
 | 
					 | 
				
			||||||
	n := len(ldapConnections)
 | 
					 | 
				
			||||||
	if n > 0 {
 | 
					 | 
				
			||||||
		conn := ldapConnections[n-1]
 | 
					 | 
				
			||||||
		ldapConnections = ldapConnections[:n-1]
 | 
					 | 
				
			||||||
		ldapConnectionsLock.Unlock()
 | 
					 | 
				
			||||||
		return conn, nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	ldapConnectionsLock.Unlock()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	conn, err := ldap.DialURL(ldapConfig.Url)
 | 
						conn, err := ldap.DialURL(ldapConfig.Url)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
@@ -65,35 +51,22 @@ func getLdapConnection() (*ldap.Conn, error) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if admin {
 | 
				
			||||||
		if err := conn.Bind(ldapConfig.SearchDN, ldapAdminPassword); err != nil {
 | 
							if err := conn.Bind(ldapConfig.SearchDN, ldapAdminPassword); err != nil {
 | 
				
			||||||
			conn.Close()
 | 
								conn.Close()
 | 
				
			||||||
			return nil, err
 | 
								return nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return conn, nil
 | 
						return conn, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func releaseConnection(conn *ldap.Conn) {
 | 
					func releaseConnection(conn *ldap.Conn) {
 | 
				
			||||||
	// Re-bind to the user we can run queries with
 | 
					 | 
				
			||||||
	if err := conn.Bind(ldapConfig.SearchDN, ldapAdminPassword); err != nil {
 | 
					 | 
				
			||||||
	conn.Close()
 | 
						conn.Close()
 | 
				
			||||||
		log.Printf("ldap error: %s", err.Error())
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ldapConnectionsLock.Lock()
 | 
					 | 
				
			||||||
	defer ldapConnectionsLock.Unlock()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	n := len(ldapConnections)
 | 
					 | 
				
			||||||
	if n > 2 {
 | 
					 | 
				
			||||||
		conn.Close()
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ldapConnections = append(ldapConnections, conn)
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func loginViaLdap(user *User, password string) error {
 | 
					func loginViaLdap(user *User, password string) error {
 | 
				
			||||||
	l, err := getLdapConnection()
 | 
						l, err := getLdapConnection(false)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -134,7 +107,7 @@ func SyncWithLDAP(db *sqlx.DB) error {
 | 
				
			|||||||
		users[username] = IN_DB
 | 
							users[username] = IN_DB
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	l, err := getLdapConnection()
 | 
						l, err := getLdapConnection(true)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -109,7 +109,7 @@ func (r *Resolver) queryJobs(ctx context.Context, filters []*model.JobFilter, pa
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func securityCheck(ctx context.Context, query sq.SelectBuilder) sq.SelectBuilder {
 | 
					func securityCheck(ctx context.Context, query sq.SelectBuilder) sq.SelectBuilder {
 | 
				
			||||||
	user := auth.GetUser(ctx)
 | 
						user := auth.GetUser(ctx)
 | 
				
			||||||
	if user == nil || user.IsAdmin {
 | 
						if user == nil || user.HasRole(auth.RoleAdmin) {
 | 
				
			||||||
		return query
 | 
							return query
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -151,7 +151,7 @@ func (r *queryResolver) Job(ctx context.Context, id string) (*schema.Job, error)
 | 
				
			|||||||
	// This query is very common (mostly called through other resolvers such as JobMetrics),
 | 
						// This query is very common (mostly called through other resolvers such as JobMetrics),
 | 
				
			||||||
	// so we use prepared statements here.
 | 
						// so we use prepared statements here.
 | 
				
			||||||
	user := auth.GetUser(ctx)
 | 
						user := auth.GetUser(ctx)
 | 
				
			||||||
	if user == nil || user.IsAdmin {
 | 
						if user == nil || user.HasRole(auth.RoleAdmin) {
 | 
				
			||||||
		return schema.ScanJob(r.findJobByIdStmt.QueryRowx(id))
 | 
							return schema.ScanJob(r.findJobByIdStmt.QueryRowx(id))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -209,7 +209,7 @@ func (r *queryResolver) RooflineHeatmap(ctx context.Context, filter []*model.Job
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func (r *queryResolver) NodeMetrics(ctx context.Context, cluster string, nodes []string, metrics []string, from time.Time, to time.Time) ([]*model.NodeMetrics, error) {
 | 
					func (r *queryResolver) NodeMetrics(ctx context.Context, cluster string, nodes []string, metrics []string, from time.Time, to time.Time) ([]*model.NodeMetrics, error) {
 | 
				
			||||||
	user := auth.GetUser(ctx)
 | 
						user := auth.GetUser(ctx)
 | 
				
			||||||
	if user != nil && !user.IsAdmin {
 | 
						if user != nil && !user.HasRole(auth.RoleAdmin) {
 | 
				
			||||||
		return nil, errors.New("you need to be an administrator for this query")
 | 
							return nil, errors.New("you need to be an administrator for this query")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -132,7 +132,7 @@ func main() {
 | 
				
			|||||||
	flag.BoolVar(&flagSyncLDAP, "sync-ldap", false, "Sync the `user` table with ldap")
 | 
						flag.BoolVar(&flagSyncLDAP, "sync-ldap", false, "Sync the `user` table with ldap")
 | 
				
			||||||
	flag.BoolVar(&flagStopImmediately, "no-server", false, "Do not start a server, stop right after initialization and argument handling")
 | 
						flag.BoolVar(&flagStopImmediately, "no-server", false, "Do not start a server, stop right after initialization and argument handling")
 | 
				
			||||||
	flag.StringVar(&flagConfigFile, "config", "", "Location of the config file for this server (overwrites the defaults)")
 | 
						flag.StringVar(&flagConfigFile, "config", "", "Location of the config file for this server (overwrites the defaults)")
 | 
				
			||||||
	flag.StringVar(&flagNewUser, "add-user", "", "Add a new user. Argument format: `<username>:[admin|api]:<password>`")
 | 
						flag.StringVar(&flagNewUser, "add-user", "", "Add a new user. Argument format: `<username>:[admin,api,user]:<password>`")
 | 
				
			||||||
	flag.StringVar(&flagDelUser, "del-user", "", "Remove user by username")
 | 
						flag.StringVar(&flagDelUser, "del-user", "", "Remove user by username")
 | 
				
			||||||
	flag.StringVar(&flagGenJWT, "jwt", "", "Generate and print a JWT for the user specified by the username")
 | 
						flag.StringVar(&flagGenJWT, "jwt", "", "Generate and print a JWT for the user specified by the username")
 | 
				
			||||||
	flag.Parse()
 | 
						flag.Parse()
 | 
				
			||||||
@@ -200,7 +200,7 @@ func main() {
 | 
				
			|||||||
				log.Fatal(err)
 | 
									log.Fatal(err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if !user.IsAPIUser {
 | 
								if !user.HasRole(auth.RoleApi) {
 | 
				
			||||||
				log.Println("warning: that user does not have the API role")
 | 
									log.Println("warning: that user does not have the API role")
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -299,7 +299,7 @@ func main() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		if user := auth.GetUser(r.Context()); user != nil {
 | 
							if user := auth.GetUser(r.Context()); user != nil {
 | 
				
			||||||
			infos["username"] = user.Username
 | 
								infos["username"] = user.Username
 | 
				
			||||||
			infos["admin"] = user.IsAdmin
 | 
								infos["admin"] = user.HasRole(auth.RoleAdmin)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		templates.Render(rw, r, "home.html", &templates.Page{
 | 
							templates.Render(rw, r, "home.html", &templates.Page{
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user