mirror of
https://github.com/ClusterCockpit/cc-backend
synced 2025-07-23 12:51:40 +02:00
Add support for multiple projects per manager
- Handled like roles in admin view - !! NEW COLUMN CHANGED TO "projects"
This commit is contained in:
@@ -9,10 +9,10 @@ import (
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
"fmt"
|
||||
|
||||
"github.com/ClusterCockpit/cc-backend/pkg/log"
|
||||
"github.com/gorilla/sessions"
|
||||
@@ -42,7 +42,7 @@ type User struct {
|
||||
Roles []string `json:"roles"`
|
||||
AuthSource int8 `json:"via"`
|
||||
Email string `json:"email"`
|
||||
Project string `json:"project"`
|
||||
Projects []string `json:"projects"`
|
||||
Expiration time.Time
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ func (u *User) HasAnyRole(queryroles []string) bool {
|
||||
|
||||
// Role-Arrays are short: performance not impacted by nested loop
|
||||
func (u *User) HasAllRoles(queryroles []string) bool {
|
||||
target := len(queryroles)
|
||||
target := len(queryroles)
|
||||
matches := 0
|
||||
for _, ur := range u.Roles {
|
||||
for _, qr := range queryroles {
|
||||
@@ -81,7 +81,7 @@ func (u *User) HasAllRoles(queryroles []string) bool {
|
||||
}
|
||||
|
||||
if matches == target {
|
||||
return true
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
@@ -100,7 +100,7 @@ func (u *User) HasNotRoles(queryroles []string) bool {
|
||||
}
|
||||
|
||||
if matches == 0 {
|
||||
return true
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
@@ -108,15 +108,15 @@ func (u *User) HasNotRoles(queryroles []string) bool {
|
||||
|
||||
// Find highest role, returns integer
|
||||
func (u *User) GetAuthLevel() int {
|
||||
if (u.HasRole(RoleAdmin)) {
|
||||
if u.HasRole(RoleAdmin) {
|
||||
return 5
|
||||
} else if (u.HasRole(RoleSupport)) {
|
||||
} else if u.HasRole(RoleSupport) {
|
||||
return 4
|
||||
} else if (u.HasRole(RoleManager)) {
|
||||
} else if u.HasRole(RoleManager) {
|
||||
return 3
|
||||
} else if (u.HasRole(RoleUser)) {
|
||||
} else if u.HasRole(RoleUser) {
|
||||
return 2
|
||||
} else if (u.HasRole(RoleApi)) {
|
||||
} else if u.HasRole(RoleApi) {
|
||||
return 1
|
||||
} else {
|
||||
return 0
|
||||
@@ -124,11 +124,12 @@ func (u *User) GetAuthLevel() int {
|
||||
}
|
||||
|
||||
func (u *User) HasProject(project string) bool {
|
||||
if (u.Project != "" && u.Project == project) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
for _, p := range u.Projects {
|
||||
if p == project {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func IsValidRole(role string) bool {
|
||||
@@ -142,7 +143,7 @@ func IsValidRole(role string) bool {
|
||||
|
||||
func GetValidRoles(user *User) ([5]string, error) {
|
||||
var vals [5]string
|
||||
if (!user.HasRole(RoleAdmin)) {
|
||||
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
|
||||
@@ -192,7 +193,7 @@ func Init(db *sqlx.DB,
|
||||
name varchar(255) DEFAULT NULL,
|
||||
roles varchar(255) NOT NULL DEFAULT "[]",
|
||||
email varchar(255) DEFAULT NULL,
|
||||
project varchar(255) DEFAULT NULL);`)
|
||||
projects varchar(255) NOT NULL DEFAULT "[]");`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -250,11 +251,11 @@ func (auth *Authentication) AuthViaSession(
|
||||
}
|
||||
|
||||
username, _ := session.Values["username"].(string)
|
||||
project, _ := session.Values["project"].(string)
|
||||
projects, _ := session.Values["projects"].([]string)
|
||||
roles, _ := session.Values["roles"].([]string)
|
||||
return &User{
|
||||
Username: username,
|
||||
Project: project,
|
||||
Projects: projects,
|
||||
Roles: roles,
|
||||
AuthSource: -1,
|
||||
}, nil
|
||||
@@ -299,7 +300,7 @@ func (auth *Authentication) Login(
|
||||
session.Options.MaxAge = int(auth.SessionMaxAge.Seconds())
|
||||
}
|
||||
session.Values["username"] = user.Username
|
||||
session.Values["project"] = user.Project
|
||||
session.Values["projects"] = user.Projects
|
||||
session.Values["roles"] = user.Roles
|
||||
if err := auth.sessionStore.Save(r, rw, session); err != nil {
|
||||
log.Errorf("session save failed: %s", err.Error())
|
||||
@@ -307,7 +308,7 @@ func (auth *Authentication) Login(
|
||||
return
|
||||
}
|
||||
|
||||
log.Infof("login successfull: user: %#v (roles: %v, project: %v)", user.Username, user.Roles, user.Project)
|
||||
log.Infof("login successfull: user: %#v (roles: %v, projects: %v)", user.Username, user.Roles, user.Projects)
|
||||
ctx := context.WithValue(r.Context(), ContextUserKey, user)
|
||||
onsuccess.ServeHTTP(rw, r.WithContext(ctx))
|
||||
return
|
||||
|
@@ -21,22 +21,26 @@ import (
|
||||
func (auth *Authentication) GetUser(username string) (*User, error) {
|
||||
|
||||
user := &User{Username: username}
|
||||
var hashedPassword, name, rawRoles, email, project sql.NullString
|
||||
if err := sq.Select("password", "ldap", "name", "roles", "email", "project").From("user").
|
||||
var hashedPassword, name, rawRoles, email, rawProjects sql.NullString
|
||||
if err := sq.Select("password", "ldap", "name", "roles", "email", "projects").From("user").
|
||||
Where("user.username = ?", username).RunWith(auth.db).
|
||||
QueryRow().Scan(&hashedPassword, &user.AuthSource, &name, &rawRoles, &email, &project); err != nil {
|
||||
QueryRow().Scan(&hashedPassword, &user.AuthSource, &name, &rawRoles, &email, &rawProjects); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
user.Password = hashedPassword.String
|
||||
user.Name = name.String
|
||||
user.Email = email.String
|
||||
user.Project = project.String
|
||||
if rawRoles.Valid {
|
||||
if err := json.Unmarshal([]byte(rawRoles.String), &user.Roles); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if rawProjects.Valid {
|
||||
if err := json.Unmarshal([]byte(rawProjects.String), &user.Projects); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
@@ -44,9 +48,11 @@ func (auth *Authentication) GetUser(username string) (*User, error) {
|
||||
func (auth *Authentication) AddUser(user *User) error {
|
||||
|
||||
rolesJson, _ := json.Marshal(user.Roles)
|
||||
projectsJson, _ := json.Marshal(user.Projects)
|
||||
|
||||
cols := []string{"username", "roles", "projects"}
|
||||
vals := []interface{}{user.Username, string(rolesJson), string(projectsJson)}
|
||||
|
||||
cols := []string{"username", "roles"}
|
||||
vals := []interface{}{user.Username, string(rolesJson)}
|
||||
if user.Name != "" {
|
||||
cols = append(cols, "name")
|
||||
vals = append(vals, user.Name)
|
||||
@@ -55,10 +61,6 @@ func (auth *Authentication) AddUser(user *User) error {
|
||||
cols = append(cols, "email")
|
||||
vals = append(vals, user.Email)
|
||||
}
|
||||
if user.Project != "" {
|
||||
cols = append(cols, "project")
|
||||
vals = append(vals, user.Project)
|
||||
}
|
||||
if user.Password != "" {
|
||||
password, err := bcrypt.GenerateFromPassword([]byte(user.Password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
@@ -72,7 +74,7 @@ func (auth *Authentication) AddUser(user *User) error {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Infof("new user %#v created (roles: %s, auth-source: %d, project: %s)", user.Username, rolesJson, user.AuthSource, user.Project)
|
||||
log.Infof("new user %#v created (roles: %s, auth-source: %d, projects: %s)", user.Username, rolesJson, user.AuthSource, projectsJson)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -84,7 +86,7 @@ func (auth *Authentication) DelUser(username string) error {
|
||||
|
||||
func (auth *Authentication) ListUsers(specialsOnly bool) ([]*User, error) {
|
||||
|
||||
q := sq.Select("username", "name", "email", "roles", "project").From("user")
|
||||
q := sq.Select("username", "name", "email", "roles", "projects").From("user")
|
||||
if specialsOnly {
|
||||
q = q.Where("(roles != '[\"user\"]' AND roles != '[]')")
|
||||
}
|
||||
@@ -98,9 +100,10 @@ func (auth *Authentication) ListUsers(specialsOnly bool) ([]*User, error) {
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
rawroles := ""
|
||||
rawprojects := ""
|
||||
user := &User{}
|
||||
var name, email, project sql.NullString
|
||||
if err := rows.Scan(&user.Username, &name, &email, &rawroles, &project); err != nil {
|
||||
var name, email sql.NullString
|
||||
if err := rows.Scan(&user.Username, &name, &email, &rawroles, &rawprojects); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -108,9 +111,12 @@ func (auth *Authentication) ListUsers(specialsOnly bool) ([]*User, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := json.Unmarshal([]byte(rawprojects), &user.Projects); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
user.Name = name.String
|
||||
user.Email = email.String
|
||||
user.Project = project.String
|
||||
users = append(users, user)
|
||||
}
|
||||
return users, nil
|
||||
@@ -151,21 +157,21 @@ func (auth *Authentication) RemoveRole(ctx context.Context, username string, rol
|
||||
return fmt.Errorf("invalid user role: %#v", role)
|
||||
}
|
||||
|
||||
if (role == RoleManager && len(user.Project) != 0) {
|
||||
return fmt.Errorf("Cannot remove role 'manager' while user %#v still has an assigned project!", username)
|
||||
if role == RoleManager && len(user.Projects) != 0 {
|
||||
return fmt.Errorf("Cannot remove role 'manager' while user %s still has an assigned project(s) : %v", username, user.Projects)
|
||||
}
|
||||
|
||||
var exists bool
|
||||
var newroles []string
|
||||
for _, r := range user.Roles {
|
||||
if r != role {
|
||||
newroles = append(newroles, r) // Append all roles not matching requested delete role
|
||||
newroles = append(newroles, r) // Append all roles not matching requested to be deleted role
|
||||
} else {
|
||||
exists = true
|
||||
}
|
||||
}
|
||||
|
||||
if (exists == true) {
|
||||
if exists == true {
|
||||
var mroles, _ = json.Marshal(newroles)
|
||||
if _, err := sq.Update("user").Set("roles", mroles).Where("user.username = ?", username).RunWith(auth.db).Exec(); err != nil {
|
||||
return err
|
||||
@@ -187,16 +193,18 @@ func (auth *Authentication) AddProject(
|
||||
}
|
||||
|
||||
if !user.HasRole(RoleManager) {
|
||||
return fmt.Errorf("user '%#v' is not a manager!", username)
|
||||
return fmt.Errorf("user '%s' is not a manager!", username)
|
||||
}
|
||||
|
||||
if user.HasProject(project) {
|
||||
return fmt.Errorf("user '%#v' already manages project '%#v'", username, project)
|
||||
return fmt.Errorf("user '%s' already manages project '%s'", username, project)
|
||||
}
|
||||
|
||||
if _, err := sq.Update("user").Set("project", project).Where("user.username = ?", username).RunWith(auth.db).Exec(); err != nil {
|
||||
projects, _ := json.Marshal(append(user.Projects, project))
|
||||
if _, err := sq.Update("user").Set("projects", projects).Where("user.username = ?", username).RunWith(auth.db).Exec(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -214,10 +222,30 @@ func (auth *Authentication) RemoveProject(ctx context.Context, username string,
|
||||
return fmt.Errorf("user '%#v': Cannot remove project '%#v' - Does not match!", username, project)
|
||||
}
|
||||
|
||||
if _, err := sq.Update("user").Set("project", "").Where("user.username = ?", username).Where("user.project = ?", project).RunWith(auth.db).Exec(); err != nil {
|
||||
return err
|
||||
var exists bool
|
||||
var newprojects []string
|
||||
for _, p := range user.Projects {
|
||||
if p != project {
|
||||
newprojects = append(newprojects, p) // Append all projects not matching requested to be deleted project
|
||||
} else {
|
||||
exists = true
|
||||
}
|
||||
}
|
||||
|
||||
if exists == true {
|
||||
var result interface{}
|
||||
if len(newprojects) == 0 {
|
||||
result = "[]"
|
||||
} else {
|
||||
result, _ = json.Marshal(newprojects)
|
||||
}
|
||||
if _, err := sq.Update("user").Set("projects", result).Where("user.username = ?", username).RunWith(auth.db).Exec(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
} else {
|
||||
return fmt.Errorf("user %s already does not manage project %s", username, project)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func FetchUser(ctx context.Context, db *sqlx.DB, username string) (*model.User, error) {
|
||||
@@ -239,6 +267,5 @@ func FetchUser(ctx context.Context, db *sqlx.DB, username string) (*model.User,
|
||||
|
||||
user.Name = name.String
|
||||
user.Email = email.String
|
||||
// user.Project = project.String
|
||||
return user, nil
|
||||
}
|
||||
|
Reference in New Issue
Block a user