mirror of
https://github.com/ClusterCockpit/cc-backend
synced 2024-12-24 12:29:05 +01:00
Rework roles as enum, change AuthSource to enum
This commit is contained in:
parent
df44bd9621
commit
f37e7c26f6
1
.gitignore
vendored
1
.gitignore
vendored
@ -3,7 +3,6 @@
|
|||||||
/var/job-archive
|
/var/job-archive
|
||||||
/var/*.db
|
/var/*.db
|
||||||
/var/machine-state
|
/var/machine-state
|
||||||
/var-backup
|
|
||||||
|
|
||||||
/.env
|
/.env
|
||||||
/config.json
|
/config.json
|
||||||
|
@ -178,7 +178,7 @@ func decode(r io.Reader, val interface{}) error {
|
|||||||
// @router /jobs/ [get]
|
// @router /jobs/ [get]
|
||||||
func (api *RestApi) getJobs(rw http.ResponseWriter, r *http.Request) {
|
func (api *RestApi) getJobs(rw http.ResponseWriter, r *http.Request) {
|
||||||
if user := auth.GetUser(r.Context()); user != nil && !user.HasRole(auth.RoleApi) {
|
if user := auth.GetUser(r.Context()); user != nil && !user.HasRole(auth.RoleApi) {
|
||||||
handleError(fmt.Errorf("missing role: %v", auth.RoleApi), http.StatusForbidden, rw)
|
handleError(fmt.Errorf("missing role: %v", auth.GetRoleString(auth.RoleApi)), http.StatusForbidden, rw)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -318,7 +318,7 @@ func (api *RestApi) getJobs(rw http.ResponseWriter, r *http.Request) {
|
|||||||
// @router /jobs/tag_job/{id} [post]
|
// @router /jobs/tag_job/{id} [post]
|
||||||
func (api *RestApi) tagJob(rw http.ResponseWriter, r *http.Request) {
|
func (api *RestApi) tagJob(rw http.ResponseWriter, r *http.Request) {
|
||||||
if user := auth.GetUser(r.Context()); user != nil && !user.HasRole(auth.RoleApi) {
|
if user := auth.GetUser(r.Context()); user != nil && !user.HasRole(auth.RoleApi) {
|
||||||
handleError(fmt.Errorf("missing role: %v", auth.RoleApi), http.StatusForbidden, rw)
|
handleError(fmt.Errorf("missing role: %v", auth.GetRoleString(auth.RoleApi)), http.StatusForbidden, rw)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -383,7 +383,7 @@ func (api *RestApi) tagJob(rw http.ResponseWriter, r *http.Request) {
|
|||||||
// @router /jobs/start_job/ [post]
|
// @router /jobs/start_job/ [post]
|
||||||
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) {
|
if user := auth.GetUser(r.Context()); user != nil && !user.HasRole(auth.RoleApi) {
|
||||||
handleError(fmt.Errorf("missing role: %v", auth.RoleApi), http.StatusForbidden, rw)
|
handleError(fmt.Errorf("missing role: %v", auth.GetRoleString(auth.RoleApi)), http.StatusForbidden, rw)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -464,7 +464,7 @@ func (api *RestApi) startJob(rw http.ResponseWriter, r *http.Request) {
|
|||||||
// @router /jobs/stop_job/{id} [post]
|
// @router /jobs/stop_job/{id} [post]
|
||||||
func (api *RestApi) stopJobById(rw http.ResponseWriter, r *http.Request) {
|
func (api *RestApi) stopJobById(rw http.ResponseWriter, r *http.Request) {
|
||||||
if user := auth.GetUser(r.Context()); user != nil && !user.HasRole(auth.RoleApi) {
|
if user := auth.GetUser(r.Context()); user != nil && !user.HasRole(auth.RoleApi) {
|
||||||
handleError(fmt.Errorf("missing role: %v", auth.RoleApi), http.StatusForbidden, rw)
|
handleError(fmt.Errorf("missing role: %v", auth.GetRoleString(auth.RoleApi)), http.StatusForbidden, rw)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -517,7 +517,7 @@ func (api *RestApi) stopJobById(rw http.ResponseWriter, r *http.Request) {
|
|||||||
// @router /jobs/stop_job/ [post]
|
// @router /jobs/stop_job/ [post]
|
||||||
func (api *RestApi) stopJobByRequest(rw http.ResponseWriter, r *http.Request) {
|
func (api *RestApi) stopJobByRequest(rw http.ResponseWriter, r *http.Request) {
|
||||||
if user := auth.GetUser(r.Context()); user != nil && !user.HasRole(auth.RoleApi) {
|
if user := auth.GetUser(r.Context()); user != nil && !user.HasRole(auth.RoleApi) {
|
||||||
handleError(fmt.Errorf("missing role: %v", auth.RoleApi), http.StatusForbidden, rw)
|
handleError(fmt.Errorf("missing role: %v", auth.GetRoleString(auth.RoleApi)), http.StatusForbidden, rw)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -563,7 +563,7 @@ func (api *RestApi) stopJobByRequest(rw http.ResponseWriter, r *http.Request) {
|
|||||||
// @router /jobs/delete_job/{id} [delete]
|
// @router /jobs/delete_job/{id} [delete]
|
||||||
func (api *RestApi) deleteJobById(rw http.ResponseWriter, r *http.Request) {
|
func (api *RestApi) deleteJobById(rw http.ResponseWriter, r *http.Request) {
|
||||||
if user := auth.GetUser(r.Context()); user != nil && !user.HasRole(auth.RoleApi) {
|
if user := auth.GetUser(r.Context()); user != nil && !user.HasRole(auth.RoleApi) {
|
||||||
handleError(fmt.Errorf("missing role: %v", auth.RoleApi), http.StatusForbidden, rw)
|
handleError(fmt.Errorf("missing role: %v", auth.GetRoleString(auth.RoleApi)), http.StatusForbidden, rw)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -611,7 +611,7 @@ func (api *RestApi) deleteJobById(rw http.ResponseWriter, r *http.Request) {
|
|||||||
// @router /jobs/delete_job/ [delete]
|
// @router /jobs/delete_job/ [delete]
|
||||||
func (api *RestApi) deleteJobByRequest(rw http.ResponseWriter, r *http.Request) {
|
func (api *RestApi) deleteJobByRequest(rw http.ResponseWriter, r *http.Request) {
|
||||||
if user := auth.GetUser(r.Context()); user != nil && !user.HasRole(auth.RoleApi) {
|
if user := auth.GetUser(r.Context()); user != nil && !user.HasRole(auth.RoleApi) {
|
||||||
handleError(fmt.Errorf("missing role: %v", auth.RoleApi), http.StatusForbidden, rw)
|
handleError(fmt.Errorf("missing role: %v", auth.GetRoleString(auth.RoleApi)), http.StatusForbidden, rw)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -667,7 +667,7 @@ func (api *RestApi) deleteJobByRequest(rw http.ResponseWriter, r *http.Request)
|
|||||||
// @router /jobs/delete_job_before/{ts} [delete]
|
// @router /jobs/delete_job_before/{ts} [delete]
|
||||||
func (api *RestApi) deleteJobBefore(rw http.ResponseWriter, r *http.Request) {
|
func (api *RestApi) deleteJobBefore(rw http.ResponseWriter, r *http.Request) {
|
||||||
if user := auth.GetUser(r.Context()); user != nil && !user.HasRole(auth.RoleApi) {
|
if user := auth.GetUser(r.Context()); user != nil && !user.HasRole(auth.RoleApi) {
|
||||||
handleError(fmt.Errorf("missing role: %v", auth.RoleApi), http.StatusForbidden, rw)
|
handleError(fmt.Errorf("missing role: %v", auth.GetRoleString(auth.RoleApi)), http.StatusForbidden, rw)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -819,15 +819,15 @@ func (api *RestApi) createUser(rw http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
username, password, role, name, email, project := r.FormValue("username"), r.FormValue("password"), r.FormValue("role"), r.FormValue("name"), r.FormValue("email"), r.FormValue("project")
|
username, password, role, name, email, project := r.FormValue("username"), r.FormValue("password"), r.FormValue("role"), r.FormValue("name"), r.FormValue("email"), r.FormValue("project")
|
||||||
if len(password) == 0 && role != auth.RoleApi {
|
if len(password) == 0 && role != auth.GetRoleString(auth.RoleApi) {
|
||||||
http.Error(rw, "Only API users are allowed to have a blank password (login will be impossible)", http.StatusBadRequest)
|
http.Error(rw, "Only API users are allowed to have a blank password (login will be impossible)", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(project) != 0 && role != auth.RoleManager {
|
if len(project) != 0 && role != auth.GetRoleString(auth.RoleManager) {
|
||||||
http.Error(rw, "only managers require a project (can be changed later)", http.StatusBadRequest)
|
http.Error(rw, "only managers require a project (can be changed later)", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
} else if len(project) == 0 && role == auth.RoleManager {
|
} else if len(project) == 0 && role == auth.GetRoleString(auth.RoleManager) {
|
||||||
http.Error(rw, "managers require a project to manage (can be changed later)", http.StatusBadRequest)
|
http.Error(rw, "managers require a project to manage (can be changed later)", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ClusterCockpit/cc-backend/pkg/log"
|
"github.com/ClusterCockpit/cc-backend/pkg/log"
|
||||||
@ -19,36 +20,83 @@ import (
|
|||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
type AuthSource int
|
||||||
RoleAdmin string = "admin"
|
|
||||||
RoleSupport string = "support"
|
|
||||||
RoleManager string = "manager"
|
|
||||||
RoleUser string = "user"
|
|
||||||
RoleApi string = "api"
|
|
||||||
)
|
|
||||||
|
|
||||||
var validRoles = [5]string{RoleUser, RoleManager, RoleSupport, RoleAdmin, RoleApi}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
AuthViaLocalPassword int8 = 0
|
AuthViaLocalPassword AuthSource = iota
|
||||||
AuthViaLDAP int8 = 1
|
AuthViaLDAP
|
||||||
AuthViaToken int8 = 2
|
AuthViaToken
|
||||||
)
|
)
|
||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
Password string `json:"-"`
|
Password string `json:"-"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Roles []string `json:"roles"`
|
Roles []string `json:"roles"`
|
||||||
AuthSource int8 `json:"via"`
|
AuthSource AuthSource `json:"via"`
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
Projects []string `json:"projects"`
|
Projects []string `json:"projects"`
|
||||||
Expiration time.Time
|
Expiration time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *User) HasRole(role string) bool {
|
type Role int
|
||||||
|
|
||||||
|
const (
|
||||||
|
RoleAnonymous Role = iota
|
||||||
|
RoleApi
|
||||||
|
RoleUser
|
||||||
|
RoleManager
|
||||||
|
RoleSupport
|
||||||
|
RoleAdmin
|
||||||
|
RoleError
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetRoleString(roleInt Role) string {
|
||||||
|
return [6]string{"anonymous", "api", "user", "manager", "support", "admin"}[roleInt]
|
||||||
|
}
|
||||||
|
|
||||||
|
func getRoleEnum(roleStr string) Role {
|
||||||
|
switch strings.ToLower(roleStr) {
|
||||||
|
case "admin":
|
||||||
|
return RoleAdmin
|
||||||
|
case "support":
|
||||||
|
return RoleSupport
|
||||||
|
case "manager":
|
||||||
|
return RoleManager
|
||||||
|
case "user":
|
||||||
|
return RoleUser
|
||||||
|
case "api":
|
||||||
|
return RoleApi
|
||||||
|
case "anonymous":
|
||||||
|
return RoleAnonymous
|
||||||
|
default:
|
||||||
|
return RoleError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func isValidRole(role string) bool {
|
||||||
|
if getRoleEnum(role) == RoleError {
|
||||||
|
log.Errorf("Unknown Role %s", role)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *User) HasValidRole(role string) (hasRole bool, isValid bool) {
|
||||||
|
if isValidRole(role) {
|
||||||
|
for _, r := range u.Roles {
|
||||||
|
if r == role {
|
||||||
|
return true, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false, true
|
||||||
|
}
|
||||||
|
return false, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *User) HasRole(role Role) bool {
|
||||||
for _, r := range u.Roles {
|
for _, r := range u.Roles {
|
||||||
if r == role {
|
if r == GetRoleString(role) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -56,10 +104,10 @@ func (u *User) HasRole(role string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Role-Arrays are short: performance not impacted by nested loop
|
// Role-Arrays are short: performance not impacted by nested loop
|
||||||
func (u *User) HasAnyRole(queryroles []string) bool {
|
func (u *User) HasAnyRole(queryroles []Role) bool {
|
||||||
for _, ur := range u.Roles {
|
for _, ur := range u.Roles {
|
||||||
for _, qr := range queryroles {
|
for _, qr := range queryroles {
|
||||||
if ur == qr {
|
if ur == GetRoleString(qr) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -68,12 +116,12 @@ func (u *User) HasAnyRole(queryroles []string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Role-Arrays are short: performance not impacted by nested loop
|
// Role-Arrays are short: performance not impacted by nested loop
|
||||||
func (u *User) HasAllRoles(queryroles []string) bool {
|
func (u *User) HasAllRoles(queryroles []Role) bool {
|
||||||
target := len(queryroles)
|
target := len(queryroles)
|
||||||
matches := 0
|
matches := 0
|
||||||
for _, ur := range u.Roles {
|
for _, ur := range u.Roles {
|
||||||
for _, qr := range queryroles {
|
for _, qr := range queryroles {
|
||||||
if ur == qr {
|
if ur == GetRoleString(qr) {
|
||||||
matches += 1
|
matches += 1
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -88,11 +136,11 @@ func (u *User) HasAllRoles(queryroles []string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Role-Arrays are short: performance not impacted by nested loop
|
// Role-Arrays are short: performance not impacted by nested loop
|
||||||
func (u *User) HasNotRoles(queryroles []string) bool {
|
func (u *User) HasNotRoles(queryroles []Role) bool {
|
||||||
matches := 0
|
matches := 0
|
||||||
for _, ur := range u.Roles {
|
for _, ur := range u.Roles {
|
||||||
for _, qr := range queryroles {
|
for _, qr := range queryroles {
|
||||||
if ur == qr {
|
if ur == GetRoleString(qr) {
|
||||||
matches += 1
|
matches += 1
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -106,20 +154,47 @@ func (u *User) HasNotRoles(queryroles []string) bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find highest role, returns integer
|
// Called by API endpoint '/roles/' from frontend: Only required for admin config -> Check Admin Role
|
||||||
func (u *User) GetAuthLevel() int {
|
func GetValidRoles(user *User) ([]string, error) {
|
||||||
|
var vals []string
|
||||||
|
if user.HasRole(RoleAdmin) {
|
||||||
|
for i := RoleApi; i < RoleError; i++ {
|
||||||
|
vals = append(vals, GetRoleString(i))
|
||||||
|
}
|
||||||
|
return vals, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return vals, fmt.Errorf("%s: only admins are allowed to fetch a list of roles", user.Username)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called by routerConfig web.page setup in backend: Only requires known user and/or not API user
|
||||||
|
func GetValidRolesMap(user *User) (map[string]Role, error) {
|
||||||
|
named := make(map[string]Role)
|
||||||
|
if user.HasNotRoles([]Role{RoleApi, RoleAnonymous}) {
|
||||||
|
for i := RoleApi; i < RoleError; i++ {
|
||||||
|
named[GetRoleString(i)] = i
|
||||||
|
}
|
||||||
|
return named, nil
|
||||||
|
}
|
||||||
|
return named, fmt.Errorf("Only known users are allowed to fetch a list of roles")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find highest role
|
||||||
|
func (u *User) GetAuthLevel() Role {
|
||||||
if u.HasRole(RoleAdmin) {
|
if u.HasRole(RoleAdmin) {
|
||||||
return 5
|
return RoleAdmin
|
||||||
} else if u.HasRole(RoleSupport) {
|
} else if u.HasRole(RoleSupport) {
|
||||||
return 4
|
return RoleSupport
|
||||||
} else if u.HasRole(RoleManager) {
|
} else if u.HasRole(RoleManager) {
|
||||||
return 3
|
return RoleManager
|
||||||
} else if u.HasRole(RoleUser) {
|
} else if u.HasRole(RoleUser) {
|
||||||
return 2
|
return RoleUser
|
||||||
} else if u.HasRole(RoleApi) {
|
} else if u.HasRole(RoleApi) {
|
||||||
return 1
|
return RoleApi
|
||||||
|
} else if u.HasRole(RoleAnonymous) {
|
||||||
|
return RoleAnonymous
|
||||||
} else {
|
} else {
|
||||||
return 0
|
return RoleError
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,24 +207,6 @@ func (u *User) HasProject(project string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsValidRole(role string) bool {
|
|
||||||
for _, r := range validRoles {
|
|
||||||
if r == role {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetValidRoles(user *User) ([5]string, error) {
|
|
||||||
var vals [5]string
|
|
||||||
if !user.HasRole(RoleAdmin) {
|
|
||||||
return vals, fmt.Errorf("%s: only admins are allowed to fetch a list of roles", user.Username)
|
|
||||||
} else {
|
|
||||||
return validRoles, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetUser(ctx context.Context) *User {
|
func GetUser(ctx context.Context) *User {
|
||||||
x := ctx.Value(ContextUserKey)
|
x := ctx.Value(ContextUserKey)
|
||||||
if x == nil {
|
if x == nil {
|
||||||
|
@ -146,12 +146,16 @@ func (ja *JWTAuthenticator) Login(
|
|||||||
if rawroles, ok := claims["roles"].([]interface{}); ok {
|
if rawroles, ok := claims["roles"].([]interface{}); ok {
|
||||||
for _, rr := range rawroles {
|
for _, rr := range rawroles {
|
||||||
if r, ok := rr.(string); ok {
|
if r, ok := rr.(string); ok {
|
||||||
roles = append(roles, r)
|
if isValidRole(r) {
|
||||||
|
roles = append(roles, r)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if rawrole, ok := claims["roles"].(string); ok {
|
if rawrole, ok := claims["roles"].(string); ok {
|
||||||
roles = append(roles, rawrole)
|
if isValidRole(rawrole) {
|
||||||
|
roles = append(roles, rawrole)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if user == nil {
|
if user == nil {
|
||||||
|
@ -164,7 +164,7 @@ func (la *LdapAuthenticator) Sync() error {
|
|||||||
name := newnames[username]
|
name := newnames[username]
|
||||||
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 := la.auth.db.Exec(`INSERT INTO user (username, ldap, name, roles) VALUES (?, ?, ?, ?)`,
|
||||||
username, 1, name, "[\""+RoleUser+"\"]"); err != nil {
|
username, 1, name, "[\""+GetRoleString(RoleUser)+"\"]"); err != nil {
|
||||||
log.Errorf("User '%s' new in LDAP: Insert into DB failed", username)
|
log.Errorf("User '%s' new in LDAP: Insert into DB failed", username)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/ClusterCockpit/cc-backend/internal/graph/model"
|
"github.com/ClusterCockpit/cc-backend/internal/graph/model"
|
||||||
"github.com/ClusterCockpit/cc-backend/pkg/log"
|
"github.com/ClusterCockpit/cc-backend/pkg/log"
|
||||||
@ -133,23 +134,25 @@ func (auth *Authentication) ListUsers(specialsOnly bool) ([]*User, error) {
|
|||||||
func (auth *Authentication) AddRole(
|
func (auth *Authentication) AddRole(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
username string,
|
username string,
|
||||||
role string) error {
|
queryrole string) error {
|
||||||
|
|
||||||
|
newRole := strings.ToLower(queryrole)
|
||||||
user, err := auth.GetUser(username)
|
user, err := auth.GetUser(username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("Could not load user '%s'", username)
|
log.Warnf("Could not load user '%s'", username)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !IsValidRole(role) {
|
exists, valid := user.HasValidRole(newRole)
|
||||||
return fmt.Errorf("Invalid user role: %v", role)
|
|
||||||
|
if !valid {
|
||||||
|
return fmt.Errorf("Supplied role is no valid option : %v", newRole)
|
||||||
|
}
|
||||||
|
if exists {
|
||||||
|
return fmt.Errorf("User %v already has role %v", username, newRole)
|
||||||
}
|
}
|
||||||
|
|
||||||
if user.HasRole(role) {
|
roles, _ := json.Marshal(append(user.Roles, newRole))
|
||||||
return fmt.Errorf("user %#v already has role %#v", username, role)
|
|
||||||
}
|
|
||||||
|
|
||||||
roles, _ := json.Marshal(append(user.Roles, role))
|
|
||||||
if _, err := sq.Update("user").Set("roles", roles).Where("user.username = ?", username).RunWith(auth.db).Exec(); err != nil {
|
if _, err := sq.Update("user").Set("roles", roles).Where("user.username = ?", username).RunWith(auth.db).Exec(); err != nil {
|
||||||
log.Errorf("Error while adding new role for user '%s'", user.Username)
|
log.Errorf("Error while adding new role for user '%s'", user.Username)
|
||||||
return err
|
return err
|
||||||
@ -157,41 +160,40 @@ func (auth *Authentication) AddRole(
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (auth *Authentication) RemoveRole(ctx context.Context, username string, role string) error {
|
func (auth *Authentication) RemoveRole(ctx context.Context, username string, queryrole string) error {
|
||||||
|
oldRole := strings.ToLower(queryrole)
|
||||||
user, err := auth.GetUser(username)
|
user, err := auth.GetUser(username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("Could not load user '%s'", username)
|
log.Warnf("Could not load user '%s'", username)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !IsValidRole(role) {
|
exists, valid := user.HasValidRole(oldRole)
|
||||||
return fmt.Errorf("Invalid user role: %#v", role)
|
|
||||||
|
if !valid {
|
||||||
|
return fmt.Errorf("Supplied role is no valid option : %v", oldRole)
|
||||||
|
}
|
||||||
|
if !exists {
|
||||||
|
return fmt.Errorf("Role already deleted for user '%v': %v", username, oldRole)
|
||||||
}
|
}
|
||||||
|
|
||||||
if role == RoleManager && len(user.Projects) != 0 {
|
if oldRole == GetRoleString(RoleManager) && len(user.Projects) != 0 {
|
||||||
return fmt.Errorf("Cannot remove role 'manager' while user %s still has assigned project(s) : %v", username, user.Projects)
|
return fmt.Errorf("Cannot remove role 'manager' while user %s still has assigned project(s) : %v", username, user.Projects)
|
||||||
}
|
}
|
||||||
|
|
||||||
var exists bool
|
|
||||||
var newroles []string
|
var newroles []string
|
||||||
for _, r := range user.Roles {
|
for _, r := range user.Roles {
|
||||||
if r != role {
|
if r != oldRole {
|
||||||
newroles = append(newroles, r) // Append all roles not matching requested to be deleted role
|
newroles = append(newroles, r) // Append all roles not matching requested to be deleted role
|
||||||
} else {
|
|
||||||
exists = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if exists == true {
|
var mroles, _ = json.Marshal(newroles)
|
||||||
var mroles, _ = json.Marshal(newroles)
|
if _, err := sq.Update("user").Set("roles", mroles).Where("user.username = ?", username).RunWith(auth.db).Exec(); err != nil {
|
||||||
if _, err := sq.Update("user").Set("roles", mroles).Where("user.username = ?", username).RunWith(auth.db).Exec(); err != nil {
|
log.Errorf("Error while removing role for user '%s'", user.Username)
|
||||||
log.Errorf("Error while removing role for user '%s'", user.Username)
|
return err
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
} else {
|
|
||||||
return fmt.Errorf("User '%v' already does not have role: %v", username, role)
|
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (auth *Authentication) AddProject(
|
func (auth *Authentication) AddProject(
|
||||||
@ -262,7 +264,7 @@ func (auth *Authentication) RemoveProject(ctx context.Context, username string,
|
|||||||
|
|
||||||
func FetchUser(ctx context.Context, db *sqlx.DB, username string) (*model.User, error) {
|
func FetchUser(ctx context.Context, db *sqlx.DB, username string) (*model.User, error) {
|
||||||
me := GetUser(ctx)
|
me := GetUser(ctx)
|
||||||
if me != nil && me.Username != username && me.HasNotRoles([]string{RoleAdmin, RoleSupport, RoleManager}) {
|
if me != nil && me.Username != username && me.HasNotRoles([]Role{RoleAdmin, RoleSupport, RoleManager}) {
|
||||||
return nil, errors.New("forbidden")
|
return nil, errors.New("forbidden")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,7 +170,7 @@ func (r *queryResolver) Job(ctx context.Context, id string) (*schema.Job, error)
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if user := auth.GetUser(ctx); user != nil && job.User != user.Username && user.HasNotRoles([]string{auth.RoleAdmin, auth.RoleSupport, auth.RoleManager}) {
|
if user := auth.GetUser(ctx); user != nil && job.User != user.Username && user.HasNotRoles([]auth.Role{auth.RoleAdmin, auth.RoleSupport, auth.RoleManager}) {
|
||||||
return nil, errors.New("you are not allowed to see this job")
|
return nil, errors.New("you are not allowed to see this job")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -534,7 +534,7 @@ func (r *JobRepository) FindColumnValue(user *auth.User, searchterm string, tabl
|
|||||||
compareStr = " LIKE ?"
|
compareStr = " LIKE ?"
|
||||||
query = "%" + searchterm + "%"
|
query = "%" + searchterm + "%"
|
||||||
}
|
}
|
||||||
if user.HasAnyRole([]string{auth.RoleAdmin, auth.RoleSupport, auth.RoleManager}) {
|
if user.HasAnyRole([]auth.Role{auth.RoleAdmin, auth.RoleSupport, auth.RoleManager}) {
|
||||||
err := sq.Select(table+"."+selectColumn).Distinct().From(table).
|
err := sq.Select(table+"."+selectColumn).Distinct().From(table).
|
||||||
Where(table+"."+whereColumn+compareStr, query).
|
Where(table+"."+whereColumn+compareStr, query).
|
||||||
RunWith(r.stmtCache).QueryRow().Scan(&result)
|
RunWith(r.stmtCache).QueryRow().Scan(&result)
|
||||||
@ -552,7 +552,7 @@ func (r *JobRepository) FindColumnValue(user *auth.User, searchterm string, tabl
|
|||||||
|
|
||||||
func (r *JobRepository) FindColumnValues(user *auth.User, query string, table string, selectColumn string, whereColumn string) (results []string, err error) {
|
func (r *JobRepository) FindColumnValues(user *auth.User, query string, table string, selectColumn string, whereColumn string) (results []string, err error) {
|
||||||
emptyResult := make([]string, 0)
|
emptyResult := make([]string, 0)
|
||||||
if user.HasAnyRole([]string{auth.RoleAdmin, auth.RoleSupport, auth.RoleManager}) {
|
if user.HasAnyRole([]auth.Role{auth.RoleAdmin, auth.RoleSupport, auth.RoleManager}) {
|
||||||
rows, err := sq.Select(table+"."+selectColumn).Distinct().From(table).
|
rows, err := sq.Select(table+"."+selectColumn).Distinct().From(table).
|
||||||
Where(table+"."+whereColumn+" LIKE ?", fmt.Sprint("%", query, "%")).
|
Where(table+"."+whereColumn+" LIKE ?", fmt.Sprint("%", query, "%")).
|
||||||
RunWith(r.stmtCache).Query()
|
RunWith(r.stmtCache).Query()
|
||||||
|
@ -104,7 +104,7 @@ func (r *JobRepository) CountJobs(
|
|||||||
|
|
||||||
func SecurityCheck(ctx context.Context, query sq.SelectBuilder) (queryOut sq.SelectBuilder, err error) {
|
func SecurityCheck(ctx context.Context, query sq.SelectBuilder) (queryOut sq.SelectBuilder, err error) {
|
||||||
user := auth.GetUser(ctx)
|
user := auth.GetUser(ctx)
|
||||||
if user == nil || user.HasAnyRole([]string{auth.RoleAdmin, auth.RoleSupport, auth.RoleApi}) { // Admin & Co. : All jobs
|
if user == nil || user.HasAnyRole([]auth.Role{auth.RoleAdmin, auth.RoleSupport, auth.RoleApi}) { // Admin & Co. : All jobs
|
||||||
return query, nil
|
return query, nil
|
||||||
} else if user.HasRole(auth.RoleManager) { // Manager : Add filter for managed projects' jobs only + personal jobs
|
} else if user.HasRole(auth.RoleManager) { // Manager : Add filter for managed projects' jobs only + personal jobs
|
||||||
if len(user.Projects) != 0 {
|
if len(user.Projects) != 0 {
|
||||||
|
@ -276,16 +276,15 @@ func SetupRoutes(router *mux.Router, version string, hash string, buildTime stri
|
|||||||
title = strings.Replace(route.Title, "<ID>", id.(string), 1)
|
title = strings.Replace(route.Title, "<ID>", id.(string), 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
username, authLevel := "", 0
|
// Get User -> What if NIL?
|
||||||
|
user := auth.GetUser(r.Context())
|
||||||
if user := auth.GetUser(r.Context()); user != nil {
|
// Get Roles
|
||||||
username = user.Username
|
availableRoles, _ := auth.GetValidRolesMap(user)
|
||||||
authLevel = user.GetAuthLevel()
|
|
||||||
}
|
|
||||||
|
|
||||||
page := web.Page{
|
page := web.Page{
|
||||||
Title: title,
|
Title: title,
|
||||||
User: web.User{Username: username, AuthLevel: authLevel},
|
User: *user,
|
||||||
|
Roles: availableRoles,
|
||||||
Build: web.Build{Version: version, Hash: hash, Buildtime: buildTime},
|
Build: web.Build{Version: version, Hash: hash, Buildtime: buildTime},
|
||||||
Config: conf,
|
Config: conf,
|
||||||
Infos: infos,
|
Infos: infos,
|
||||||
@ -314,7 +313,7 @@ func HandleSearchBar(rw http.ResponseWriter, r *http.Request, api *api.RestApi)
|
|||||||
case "projectId":
|
case "projectId":
|
||||||
http.Redirect(rw, r, "/monitoring/jobs/?projectMatch=eq&project="+url.QueryEscape(strings.Trim(splitSearch[1], " ")), http.StatusTemporaryRedirect) // All Users: Redirect to Tablequery
|
http.Redirect(rw, r, "/monitoring/jobs/?projectMatch=eq&project="+url.QueryEscape(strings.Trim(splitSearch[1], " ")), http.StatusTemporaryRedirect) // All Users: Redirect to Tablequery
|
||||||
case "username":
|
case "username":
|
||||||
if user.HasAnyRole([]string{auth.RoleAdmin, auth.RoleSupport, auth.RoleManager}) {
|
if user.HasAnyRole([]auth.Role{auth.RoleAdmin, auth.RoleSupport, auth.RoleManager}) {
|
||||||
http.Redirect(rw, r, "/monitoring/users/?user="+url.QueryEscape(strings.Trim(splitSearch[1], " ")), http.StatusTemporaryRedirect)
|
http.Redirect(rw, r, "/monitoring/users/?user="+url.QueryEscape(strings.Trim(splitSearch[1], " ")), http.StatusTemporaryRedirect)
|
||||||
} else {
|
} else {
|
||||||
http.Redirect(rw, r, "/monitoring/jobs/?", http.StatusTemporaryRedirect) // Users: Redirect to Tablequery
|
http.Redirect(rw, r, "/monitoring/jobs/?", http.StatusTemporaryRedirect) // Users: Redirect to Tablequery
|
||||||
@ -325,7 +324,7 @@ func HandleSearchBar(rw http.ResponseWriter, r *http.Request, api *api.RestApi)
|
|||||||
joinedNames := strings.Join(usernames, "&user=")
|
joinedNames := strings.Join(usernames, "&user=")
|
||||||
http.Redirect(rw, r, "/monitoring/users/?user="+joinedNames, http.StatusTemporaryRedirect)
|
http.Redirect(rw, r, "/monitoring/users/?user="+joinedNames, http.StatusTemporaryRedirect)
|
||||||
} else {
|
} else {
|
||||||
if user.HasAnyRole([]string{auth.RoleAdmin, auth.RoleSupport, auth.RoleManager}) {
|
if user.HasAnyRole([]auth.Role{auth.RoleAdmin, auth.RoleSupport, auth.RoleManager}) {
|
||||||
http.Redirect(rw, r, "/monitoring/users/?user=NoUserNameFound", http.StatusTemporaryRedirect)
|
http.Redirect(rw, r, "/monitoring/users/?user=NoUserNameFound", http.StatusTemporaryRedirect)
|
||||||
} else {
|
} else {
|
||||||
http.Redirect(rw, r, "/monitoring/jobs/?", http.StatusTemporaryRedirect) // Users: Redirect to Tablequery
|
http.Redirect(rw, r, "/monitoring/jobs/?", http.StatusTemporaryRedirect) // Users: Redirect to Tablequery
|
||||||
|
@ -10,11 +10,11 @@
|
|||||||
|
|
||||||
const ccconfig = getContext('cc-config')
|
const ccconfig = getContext('cc-config')
|
||||||
|
|
||||||
export let user
|
export let isAdmin
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if user.AuthLevel == 5}
|
{#if isAdmin == true}
|
||||||
<Card style="margin-bottom: 1.5em;">
|
<Card style="margin-bottom: 1.5em;">
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle class="mb-1">Admin Options</CardTitle>
|
<CardTitle class="mb-1">Admin Options</CardTitle>
|
||||||
|
@ -4,8 +4,9 @@
|
|||||||
Dropdown, DropdownToggle, DropdownMenu, DropdownItem, InputGroupText } from 'sveltestrap'
|
Dropdown, DropdownToggle, DropdownMenu, DropdownItem, InputGroupText } from 'sveltestrap'
|
||||||
|
|
||||||
export let username // empty string if auth. is disabled, otherwise the username as string
|
export let username // empty string if auth. is disabled, otherwise the username as string
|
||||||
export let authlevel // integer
|
export let authlevel // Integer
|
||||||
export let clusters // array of names
|
export let clusters // array of names
|
||||||
|
export let roles // Role Enum-Like
|
||||||
|
|
||||||
let isOpen = false
|
let isOpen = false
|
||||||
|
|
||||||
@ -39,9 +40,9 @@
|
|||||||
]
|
]
|
||||||
|
|
||||||
const viewsPerCluster = [
|
const viewsPerCluster = [
|
||||||
{ title: 'Analysis', authLevel: 4, href: '/monitoring/analysis/', icon: 'graph-up' },
|
{ title: 'Analysis', requiredRole: roles.support, href: '/monitoring/analysis/', icon: 'graph-up' },
|
||||||
{ title: 'Systems', authLevel: 5, href: '/monitoring/systems/', icon: 'cpu' },
|
{ title: 'Systems', requiredRole: roles.admin, href: '/monitoring/systems/', icon: 'cpu' },
|
||||||
{ title: 'Status', authLevel: 5, href: '/monitoring/status/', icon: 'cpu' },
|
{ title: 'Status', requiredRole: roles.admin, href: '/monitoring/status/', icon: 'cpu' },
|
||||||
]
|
]
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -52,26 +53,26 @@
|
|||||||
<NavbarToggler on:click={() => (isOpen = !isOpen)} />
|
<NavbarToggler on:click={() => (isOpen = !isOpen)} />
|
||||||
<Collapse {isOpen} navbar expand="lg" on:update={({ detail }) => (isOpen = detail.isOpen)}>
|
<Collapse {isOpen} navbar expand="lg" on:update={({ detail }) => (isOpen = detail.isOpen)}>
|
||||||
<Nav pills>
|
<Nav pills>
|
||||||
{#if authlevel == 5} <!-- admin -->
|
{#if authlevel == roles.admin}
|
||||||
{#each adminviews as item}
|
{#each adminviews as item}
|
||||||
<NavLink href={item.href} active={window.location.pathname == item.href}><Icon name={item.icon}/> {item.title}</NavLink>
|
<NavLink href={item.href} active={window.location.pathname == item.href}><Icon name={item.icon}/> {item.title}</NavLink>
|
||||||
{/each}
|
{/each}
|
||||||
{:else if authlevel == 4} <!-- support -->
|
{:else if authlevel == roles.support}
|
||||||
{#each supportviews as item}
|
{#each supportviews as item}
|
||||||
<NavLink href={item.href} active={window.location.pathname == item.href}><Icon name={item.icon}/> {item.title}</NavLink>
|
<NavLink href={item.href} active={window.location.pathname == item.href}><Icon name={item.icon}/> {item.title}</NavLink>
|
||||||
{/each}
|
{/each}
|
||||||
{:else if authlevel == 3} <!-- manager -->
|
{:else if authlevel == roles.manager}
|
||||||
{#each managerviews as item}
|
{#each managerviews as item}
|
||||||
<NavLink href={item.href} active={window.location.pathname == item.href}><Icon name={item.icon}/> {item.title}</NavLink>
|
<NavLink href={item.href} active={window.location.pathname == item.href}><Icon name={item.icon}/> {item.title}</NavLink>
|
||||||
{/each}
|
{/each}
|
||||||
{:else if authlevel == 2} <!-- user -->
|
{:else if authlevel == roles.user}
|
||||||
{#each userviews as item}
|
{#each userviews as item}
|
||||||
<NavLink href={item.href} active={window.location.pathname == item.href}><Icon name={item.icon}/> {item.title}</NavLink>
|
<NavLink href={item.href} active={window.location.pathname == item.href}><Icon name={item.icon}/> {item.title}</NavLink>
|
||||||
{/each}
|
{/each}
|
||||||
{:else}
|
{:else}
|
||||||
<p>API User or Unauthorized!</p>
|
<p>API User or Unauthorized!</p>
|
||||||
{/if}
|
{/if}
|
||||||
{#each viewsPerCluster.filter(item => item.authLevel <= authlevel) as item}
|
{#each viewsPerCluster.filter(item => item.requiredRole <= authlevel) as item}
|
||||||
<NavItem>
|
<NavItem>
|
||||||
<Dropdown nav inNavbar>
|
<Dropdown nav inNavbar>
|
||||||
<DropdownToggle nav caret>
|
<DropdownToggle nav caret>
|
||||||
@ -94,7 +95,7 @@
|
|||||||
<InputGroup>
|
<InputGroup>
|
||||||
<Input type="text" placeholder="Search 'type:<query>' ..." name="searchId"/>
|
<Input type="text" placeholder="Search 'type:<query>' ..." name="searchId"/>
|
||||||
<Button outline type="submit"><Icon name="search"/></Button>
|
<Button outline type="submit"><Icon name="search"/></Button>
|
||||||
<InputGroupText style="cursor:help;" title={(authlevel >= 4) ? "Example: 'projectId:a100cd', Types are: jobId | jobName | projectId | username | name" : "Example: 'jobName:myjob', Types are jobId | jobName | projectId"}><Icon name="info-circle"/></InputGroupText>
|
<InputGroupText style="cursor:help;" title={(authlevel >= roles.support) ? "Example: 'projectId:a100cd', Types are: jobId | jobName | projectId | username | name" : "Example: 'jobName:myjob', Types are jobId | jobName | projectId"}><Icon name="info-circle"/></InputGroupText>
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
</form>
|
</form>
|
||||||
{#if username}
|
{#if username}
|
||||||
|
@ -14,7 +14,8 @@
|
|||||||
const ccconfig = getContext('cc-config')
|
const ccconfig = getContext('cc-config')
|
||||||
|
|
||||||
export let filterPresets = {}
|
export let filterPresets = {}
|
||||||
export let authLevel
|
export let authlevel
|
||||||
|
export let roles
|
||||||
|
|
||||||
let filters, jobList, matchedJobs = null
|
let filters, jobList, matchedJobs = null
|
||||||
let sorting = { field: 'startTime', order: 'DESC' }, isSortingOpen = false, isMetricsSelectionOpen = false
|
let sorting = { field: 'startTime', order: 'DESC' }, isSortingOpen = false, isMetricsSelectionOpen = false
|
||||||
@ -61,7 +62,7 @@
|
|||||||
</Col>
|
</Col>
|
||||||
|
|
||||||
<Col xs="3" style="margin-left: auto;">
|
<Col xs="3" style="margin-left: auto;">
|
||||||
<UserOrProject bind:authLevel={authLevel} on:update={({ detail }) => filters.update(detail)}/>
|
<UserOrProject bind:authlevel={authlevel} bind:roles={roles} on:update={({ detail }) => filters.update(detail)}/>
|
||||||
</Col>
|
</Col>
|
||||||
<Col xs="2">
|
<Col xs="2">
|
||||||
<Refresher on:reload={() => jobList.update()} />
|
<Refresher on:reload={() => jobList.update()} />
|
||||||
|
@ -4,7 +4,7 @@ import Config from './Config.root.svelte'
|
|||||||
new Config({
|
new Config({
|
||||||
target: document.getElementById('svelte-app'),
|
target: document.getElementById('svelte-app'),
|
||||||
props: {
|
props: {
|
||||||
user: user
|
isAdmin: isAdmin
|
||||||
},
|
},
|
||||||
context: new Map([
|
context: new Map([
|
||||||
['cc-config', clusterCockpitConfig]
|
['cc-config', clusterCockpitConfig]
|
||||||
|
@ -6,7 +6,8 @@
|
|||||||
|
|
||||||
export let user = ''
|
export let user = ''
|
||||||
export let project = ''
|
export let project = ''
|
||||||
export let authLevel
|
export let authlevel
|
||||||
|
export let roles
|
||||||
let mode = 'user', term = ''
|
let mode = 'user', term = ''
|
||||||
const throttle = 500
|
const throttle = 500
|
||||||
|
|
||||||
@ -23,7 +24,7 @@
|
|||||||
|
|
||||||
let timeoutId = null
|
let timeoutId = null
|
||||||
function termChanged(sleep = throttle) {
|
function termChanged(sleep = throttle) {
|
||||||
if (authLevel == 2) {
|
if (authlevel == roles.user) {
|
||||||
project = term
|
project = term
|
||||||
|
|
||||||
if (timeoutId != null)
|
if (timeoutId != null)
|
||||||
@ -34,7 +35,7 @@
|
|||||||
project
|
project
|
||||||
})
|
})
|
||||||
}, sleep)
|
}, sleep)
|
||||||
} else if (authLevel >= 3) {
|
} else if (authlevel >= roles.manager) {
|
||||||
if (mode == 'user')
|
if (mode == 'user')
|
||||||
user = term
|
user = term
|
||||||
else
|
else
|
||||||
@ -53,13 +54,13 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if authLevel == 2}
|
{#if authlevel == roles.user}
|
||||||
<InputGroup>
|
<InputGroup>
|
||||||
<Input
|
<Input
|
||||||
type="text" bind:value={term} on:change={() => termChanged()} on:keyup={(event) => termChanged(event.key == 'Enter' ? 0 : throttle)} placeholder='filter project...'
|
type="text" bind:value={term} on:change={() => termChanged()} on:keyup={(event) => termChanged(event.key == 'Enter' ? 0 : throttle)} placeholder='filter project...'
|
||||||
/>
|
/>
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
{:else if authLevel >= 3}
|
{:else if authlevel >= roles.manager}
|
||||||
<InputGroup>
|
<InputGroup>
|
||||||
<select style="max-width: 175px;" class="form-select"
|
<select style="max-width: 175px;" class="form-select"
|
||||||
bind:value={mode} on:change={modeChanged}>
|
bind:value={mode} on:change={modeChanged}>
|
||||||
|
@ -5,7 +5,8 @@ new Jobs({
|
|||||||
target: document.getElementById('svelte-app'),
|
target: document.getElementById('svelte-app'),
|
||||||
props: {
|
props: {
|
||||||
filterPresets: filterPresets,
|
filterPresets: filterPresets,
|
||||||
authLevel: authLevel
|
authlevel: authlevel,
|
||||||
|
roles: roles
|
||||||
},
|
},
|
||||||
context: new Map([
|
context: new Map([
|
||||||
['cc-config', clusterCockpitConfig]
|
['cc-config', clusterCockpitConfig]
|
||||||
|
@ -15,9 +15,10 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
<script>
|
<script>
|
||||||
const header = {
|
const header = {
|
||||||
"username": "{{ .User.Username }}",
|
"username": "{{ .User.Username }}",
|
||||||
"authlevel": {{ .User.AuthLevel }},
|
"authlevel": {{ .User.GetAuthLevel }},
|
||||||
"clusters": {{ .Clusters }},
|
"clusters": {{ .Clusters }},
|
||||||
|
"roles": {{ .Roles }}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
{{define "javascript"}}
|
{{define "javascript"}}
|
||||||
<script>
|
<script>
|
||||||
const user = {{ .User }};
|
const isAdmin = {{ .User.HasRole .Roles.admin }};
|
||||||
const filterPresets = {{ .FilterPresets }};
|
const filterPresets = {{ .FilterPresets }};
|
||||||
const clusterCockpitConfig = {{ .Config }};
|
const clusterCockpitConfig = {{ .Config }};
|
||||||
</script>
|
</script>
|
||||||
|
@ -9,14 +9,14 @@
|
|||||||
<th>Running Jobs (short ones not listed)</th>
|
<th>Running Jobs (short ones not listed)</th>
|
||||||
<th>Total Jobs</th>
|
<th>Total Jobs</th>
|
||||||
<th>Short Jobs in past 24h</th>
|
<th>Short Jobs in past 24h</th>
|
||||||
{{if ge .User.AuthLevel 4}}
|
{{if .User.HasRole .Roles.admin}}
|
||||||
<th>System View</th>
|
<th>System View</th>
|
||||||
<th>Analysis View</th>
|
<th>Analysis View</th>
|
||||||
{{end}}
|
{{end}}
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{{if ge .User.AuthLevel 4}}
|
{{if .User.HasRole .Roles.admin}}
|
||||||
{{range .Infos.clusters}}
|
{{range .Infos.clusters}}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{.Name}}</td>
|
<td>{{.Name}}</td>
|
||||||
|
@ -10,7 +10,8 @@
|
|||||||
<script>
|
<script>
|
||||||
const filterPresets = {{ .FilterPresets }};
|
const filterPresets = {{ .FilterPresets }};
|
||||||
const clusterCockpitConfig = {{ .Config }};
|
const clusterCockpitConfig = {{ .Config }};
|
||||||
const authLevel = {{ .User.AuthLevel }};
|
const authlevel = {{ .User.GetAuthLevel }};
|
||||||
|
const roles = {{ .Roles }};
|
||||||
</script>
|
</script>
|
||||||
<script src='/build/jobs.js'></script>
|
<script src='/build/jobs.js'></script>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
@ -11,6 +11,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/ClusterCockpit/cc-backend/internal/auth"
|
||||||
"github.com/ClusterCockpit/cc-backend/internal/config"
|
"github.com/ClusterCockpit/cc-backend/internal/config"
|
||||||
"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"
|
||||||
@ -55,11 +56,6 @@ func init() {
|
|||||||
_ = base
|
_ = base
|
||||||
}
|
}
|
||||||
|
|
||||||
type User struct {
|
|
||||||
Username string // Username of the currently logged in user
|
|
||||||
AuthLevel int // Level of authorization
|
|
||||||
}
|
|
||||||
|
|
||||||
type Build struct {
|
type Build struct {
|
||||||
Version string
|
Version string
|
||||||
Hash string
|
Hash string
|
||||||
@ -70,7 +66,8 @@ type Page struct {
|
|||||||
Title string // Page title
|
Title string // Page title
|
||||||
Error string // For generic use (e.g. the exact error message on /login)
|
Error string // For generic use (e.g. the exact error message on /login)
|
||||||
Info string // For generic use (e.g. "Logout successfull" on /login)
|
Info string // For generic use (e.g. "Logout successfull" on /login)
|
||||||
User User // Information about the currently logged in user
|
User auth.User // Information about the currently logged in user (Full User Info)
|
||||||
|
Roles map[string]auth.Role // Available roles for frontend render checks
|
||||||
Build Build // Latest information about the application
|
Build Build // Latest information about the application
|
||||||
Clusters []schema.ClusterConfig // List of all clusters for use in the Header
|
Clusters []schema.ClusterConfig // List of all clusters for use in the Header
|
||||||
FilterPresets map[string]interface{} // For pages with the Filter component, this can be used to set initial filters.
|
FilterPresets map[string]interface{} // For pages with the Filter component, this can be used to set initial filters.
|
||||||
|
Loading…
Reference in New Issue
Block a user