mirror of
https://github.com/ClusterCockpit/cc-backend
synced 2024-12-25 12:59:06 +01:00
Some new REST endpoints; cleanup
This commit is contained in:
parent
5f12b49145
commit
046d4d5187
46
api/rest.go
46
api/rest.go
@ -51,8 +51,10 @@ func (api *RestApi) MountRoutes(r *mux.Router) {
|
||||
r.HandleFunc("/jobs/metrics/{id}", api.getJobMetrics).Methods(http.MethodGet)
|
||||
|
||||
if api.Authentication != nil {
|
||||
r.HandleFunc("/jwt/", api.getJWT).Methods(http.MethodPost)
|
||||
r.HandleFunc("/jwt/", api.getJWT).Methods(http.MethodGet)
|
||||
r.HandleFunc("/users/", api.createUser).Methods(http.MethodPost, http.MethodPut)
|
||||
r.HandleFunc("/users/", api.getUsers).Methods(http.MethodGet)
|
||||
r.HandleFunc("/users/", api.deleteUser).Methods(http.MethodDelete)
|
||||
r.HandleFunc("/configuration/", api.updateConfiguration).Methods(http.MethodPost)
|
||||
}
|
||||
|
||||
@ -474,6 +476,7 @@ func (api *RestApi) getJobMetrics(rw http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func (api *RestApi) getJWT(rw http.ResponseWriter, r *http.Request) {
|
||||
rw.Header().Set("Content-Type", "text/plain")
|
||||
username := r.FormValue("username")
|
||||
me := auth.GetUser(r.Context())
|
||||
if !me.HasRole(auth.RoleAdmin) {
|
||||
@ -500,19 +503,35 @@ func (api *RestApi) getJWT(rw http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func (api *RestApi) createUser(rw http.ResponseWriter, r *http.Request) {
|
||||
rw.Header().Set("Content-Type", "text/plain")
|
||||
me := auth.GetUser(r.Context())
|
||||
if !me.HasRole(auth.RoleAdmin) {
|
||||
http.Error(rw, "only admins are allowed to create new users", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
username, password, role := r.FormValue("username"), r.FormValue("password"), r.FormValue("role")
|
||||
username, password, role, name, email := r.FormValue("username"), r.FormValue("password"), r.FormValue("role"), r.FormValue("name"), r.FormValue("email")
|
||||
if len(password) == 0 && role != auth.RoleApi {
|
||||
http.Error(rw, "only API users are allowed to have a blank password (login will be impossible)", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if err := api.Authentication.AddUser(username + ":" + role + ":" + password); err != nil {
|
||||
if err := api.Authentication.CreateUser(username, name, password, email, []string{role}); err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusUnprocessableEntity)
|
||||
return
|
||||
}
|
||||
|
||||
rw.Write([]byte(fmt.Sprintf("User %#v successfully created!\n", username)))
|
||||
}
|
||||
|
||||
func (api *RestApi) deleteUser(rw http.ResponseWriter, r *http.Request) {
|
||||
if user := auth.GetUser(r.Context()); !user.HasRole(auth.RoleAdmin) {
|
||||
http.Error(rw, "only admins are allowed to delete a user", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
username := r.FormValue("username")
|
||||
if err := api.Authentication.DelUser(username); err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusUnprocessableEntity)
|
||||
return
|
||||
}
|
||||
@ -520,12 +539,33 @@ func (api *RestApi) createUser(rw http.ResponseWriter, r *http.Request) {
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
||||
func (api *RestApi) getUsers(rw http.ResponseWriter, r *http.Request) {
|
||||
if user := auth.GetUser(r.Context()); !user.HasRole(auth.RoleAdmin) {
|
||||
http.Error(rw, "only admins are allowed to fetch a list of users", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
users, err := api.Authentication.FetchUsers(r.URL.Query().Get("via-ldap") == "true")
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
json.NewEncoder(rw).Encode(users)
|
||||
}
|
||||
|
||||
func (api *RestApi) updateConfiguration(rw http.ResponseWriter, r *http.Request) {
|
||||
rw.Header().Set("Content-Type", "text/plain")
|
||||
key, value := r.FormValue("key"), r.FormValue("value")
|
||||
|
||||
fmt.Printf("KEY: %#v\nVALUE: %#v\n", key, value)
|
||||
|
||||
if err := config.UpdateConfig(key, value, r.Context()); err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusUnprocessableEntity)
|
||||
return
|
||||
}
|
||||
|
||||
rw.Write([]byte("success"))
|
||||
}
|
||||
|
||||
func (api *RestApi) putMachineState(rw http.ResponseWriter, r *http.Request) {
|
||||
|
92
auth/auth.go
92
auth/auth.go
@ -26,12 +26,12 @@ import (
|
||||
// If Name and Email is needed as well, use auth.FetchUser(), which does a database
|
||||
// query for all fields.
|
||||
type User struct {
|
||||
Username string
|
||||
Password string
|
||||
Name string
|
||||
Roles []string
|
||||
ViaLdap bool
|
||||
Email string
|
||||
Username string `json:"username"`
|
||||
Password string `json:"-"`
|
||||
Name string `json:"name"`
|
||||
Roles []string `json:"roles"`
|
||||
ViaLdap bool `json:"via-ldap"`
|
||||
Email string `json:"email"`
|
||||
}
|
||||
|
||||
const (
|
||||
@ -130,32 +130,46 @@ func (auth *Authentication) AddUser(arg string) error {
|
||||
return errors.New("invalid argument format")
|
||||
}
|
||||
|
||||
password := ""
|
||||
if len(parts[2]) > 0 {
|
||||
bytes, err := bcrypt.GenerateFromPassword([]byte(parts[2]), bcrypt.DefaultCost)
|
||||
roles := strings.Split(parts[1], ",")
|
||||
return auth.CreateUser(parts[0], "", parts[2], "", roles)
|
||||
}
|
||||
|
||||
func (auth *Authentication) CreateUser(username, name, password, email string, roles []string) error {
|
||||
for _, role := range roles {
|
||||
if role != RoleAdmin && role != RoleApi && role != RoleUser {
|
||||
return fmt.Errorf("invalid user role: %#v", role)
|
||||
}
|
||||
}
|
||||
|
||||
if username == "" {
|
||||
return errors.New("username should not be empty")
|
||||
}
|
||||
|
||||
if password != "" {
|
||||
bytes, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
password = string(bytes)
|
||||
}
|
||||
|
||||
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)
|
||||
cols := []string{"username", "password", "roles"}
|
||||
vals := []interface{}{username, password, string(rolesJson)}
|
||||
if name != "" {
|
||||
cols = append(cols, "name")
|
||||
vals = append(vals, name)
|
||||
}
|
||||
if email != "" {
|
||||
cols = append(cols, "email")
|
||||
vals = append(vals, email)
|
||||
}
|
||||
|
||||
rolesJson, _ := json.Marshal(roles)
|
||||
_, err := sq.Insert("user").Columns("username", "password", "roles").Values(parts[0], password, string(rolesJson)).RunWith(auth.db).Exec()
|
||||
if err != nil {
|
||||
if _, err := sq.Insert("user").Columns(cols...).Values(vals...).RunWith(auth.db).Exec(); err != nil {
|
||||
return err
|
||||
}
|
||||
log.Infof("new user %#v added (roles: %s)", parts[0], roles)
|
||||
|
||||
log.Infof("new user %#v created (roles: %s)", username, roles)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -164,6 +178,40 @@ func (auth *Authentication) DelUser(username string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (auth *Authentication) FetchUsers(viaLdap bool) ([]*User, error) {
|
||||
q := sq.Select("username", "name", "email", "roles").From("user")
|
||||
if !viaLdap {
|
||||
q = q.Where("ldap = 0")
|
||||
} else {
|
||||
q = q.Where("ldap = 1")
|
||||
}
|
||||
|
||||
rows, err := q.RunWith(auth.db).Query()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
users := make([]*User, 0)
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
rawroles := ""
|
||||
user := &User{}
|
||||
var name, email sql.NullString
|
||||
if err := rows.Scan(&user.Username, &name, &email, &rawroles); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := json.Unmarshal([]byte(rawroles), &user.Roles); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
user.Name = name.String
|
||||
user.Email = email.String
|
||||
users = append(users, user)
|
||||
}
|
||||
return users, nil
|
||||
}
|
||||
|
||||
func (auth *Authentication) FetchUser(username string) (*User, error) {
|
||||
user := &User{Username: username}
|
||||
var hashedPassword, name, rawRoles, email sql.NullString
|
||||
|
@ -132,6 +132,7 @@ func GetUIConfig(r *http.Request) (map[string]interface{}, error) {
|
||||
}
|
||||
|
||||
size := 0
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
var key, rawval string
|
||||
if err := rows.Scan(&key, &rawval); err != nil {
|
||||
@ -173,12 +174,16 @@ func UpdateConfig(key, value string, ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
cache.Del(user.Username)
|
||||
if _, ok := uiDefaults[key]; !ok {
|
||||
return errors.New("this configuration key does not exist")
|
||||
}
|
||||
|
||||
if _, err := db.Exec(`REPLACE INTO configuration (username, confkey, value) VALUES (?, ?, ?)`,
|
||||
user.Username, key, value); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cache.Del(user.Username)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -60,6 +60,7 @@ func (r *JobRepository) QueryJobs(
|
||||
for rows.Next() {
|
||||
job, err := scanJob(rows)
|
||||
if err != nil {
|
||||
rows.Close()
|
||||
return nil, err
|
||||
}
|
||||
jobs = append(jobs, job)
|
||||
|
@ -1,8 +1,6 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/ClusterCockpit/cc-backend/metricdata"
|
||||
"github.com/ClusterCockpit/cc-backend/schema"
|
||||
sq "github.com/Masterminds/squirrel"
|
||||
@ -88,9 +86,8 @@ func (r *JobRepository) CountTags(user *string) (tags []schema.Tag, counts map[s
|
||||
for rows.Next() {
|
||||
var tagName string
|
||||
var count int
|
||||
err = rows.Scan(&tagName, &count)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
if err := rows.Scan(&tagName, &count); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
counts[tagName] = count
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user