mirror of
https://github.com/ClusterCockpit/cc-backend
synced 2026-06-06 11:47:29 +02:00
Merge pull request #552 from ClusterCockpit/fix/add-user-edit-api
Reintroduce user update api path
This commit is contained in:
@@ -958,8 +958,11 @@
|
||||
"/api/user/{id}": {
|
||||
"post": {
|
||||
"description": "Allows admins to add/remove roles and projects for a user",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"text/plain"
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"User"
|
||||
@@ -974,35 +977,26 @@
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Role to add",
|
||||
"name": "add-role",
|
||||
"in": "formData"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Role to remove",
|
||||
"name": "remove-role",
|
||||
"in": "formData"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Project to add",
|
||||
"name": "add-project",
|
||||
"in": "formData"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Project to remove",
|
||||
"name": "remove-project",
|
||||
"in": "formData"
|
||||
"description": "Single Field Changes",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.UpdateUserAPIRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Success message",
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/api.DefaultAPIResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
@@ -1933,6 +1927,31 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"api.UpdateUserAPIRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"add-role": {
|
||||
"description": "Role to add to user $ID",
|
||||
"type": "string",
|
||||
"example": "user"
|
||||
},
|
||||
"remove-role": {
|
||||
"description": "Role to remove from user $ID",
|
||||
"type": "string",
|
||||
"example": "user"
|
||||
},
|
||||
"add-project": {
|
||||
"description": "Project to add to user $ID managed array",
|
||||
"type": "string",
|
||||
"example": "abcd100"
|
||||
},
|
||||
"remove-project": {
|
||||
"description": "Project to remove from user $ID managed array",
|
||||
"type": "string",
|
||||
"example": "abcd100"
|
||||
}
|
||||
}
|
||||
},
|
||||
"api.DefaultAPIResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
||||
@@ -31,6 +31,25 @@ definitions:
|
||||
example: Debug
|
||||
type: string
|
||||
type: object
|
||||
api.UpdateUserAPIRequest:
|
||||
properties:
|
||||
add-project:
|
||||
description: Project to add to user $ID managed array
|
||||
example: abcd100
|
||||
type: string
|
||||
add-role:
|
||||
description: Role to add to user $ID
|
||||
example: user
|
||||
type: string
|
||||
remove-project:
|
||||
description: Project to remove from user $ID managed array
|
||||
example: abcd100
|
||||
type: string
|
||||
remove-role:
|
||||
description: Role to remove from user $ID
|
||||
example: user
|
||||
type: string
|
||||
type: object
|
||||
api.DefaultAPIResponse:
|
||||
properties:
|
||||
msg:
|
||||
@@ -1388,6 +1407,8 @@ paths:
|
||||
- Nodestates
|
||||
/api/user/{id}:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Allows admins to add/remove roles and projects for a user
|
||||
parameters:
|
||||
- description: Username
|
||||
@@ -1395,29 +1416,23 @@ paths:
|
||||
name: id
|
||||
required: true
|
||||
type: string
|
||||
- description: Role to add
|
||||
in: formData
|
||||
name: add-role
|
||||
type: string
|
||||
- description: Role to remove
|
||||
in: formData
|
||||
name: remove-role
|
||||
type: string
|
||||
- description: Project to add
|
||||
in: formData
|
||||
name: add-project
|
||||
type: string
|
||||
- description: Project to remove
|
||||
in: formData
|
||||
name: remove-project
|
||||
type: string
|
||||
- description: Single Field Changes
|
||||
in: body
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/api.UpdateUserAPIRequest'
|
||||
produces:
|
||||
- text/plain
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: Success message
|
||||
description: OK
|
||||
schema:
|
||||
type: string
|
||||
$ref: '#/definitions/api.DefaultAPIResponse'
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
$ref: '#/definitions/api.ErrorResponse'
|
||||
"403":
|
||||
description: Forbidden
|
||||
schema:
|
||||
|
||||
@@ -965,8 +965,11 @@ const docTemplate = `{
|
||||
"/api/user/{id}": {
|
||||
"post": {
|
||||
"description": "Allows admins to add/remove roles and projects for a user",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"text/plain"
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"User"
|
||||
@@ -981,35 +984,26 @@ const docTemplate = `{
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Role to add",
|
||||
"name": "add-role",
|
||||
"in": "formData"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Role to remove",
|
||||
"name": "remove-role",
|
||||
"in": "formData"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Project to add",
|
||||
"name": "add-project",
|
||||
"in": "formData"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Project to remove",
|
||||
"name": "remove-project",
|
||||
"in": "formData"
|
||||
"description": "Single Field Changes",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.UpdateUserAPIRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Success message",
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/api.DefaultAPIResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
@@ -1940,6 +1934,31 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"api.UpdateUserAPIRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"add-role": {
|
||||
"description": "Role to add to user $ID",
|
||||
"type": "string",
|
||||
"example": "user"
|
||||
},
|
||||
"remove-role": {
|
||||
"description": "Role to remove from user $ID",
|
||||
"type": "string",
|
||||
"example": "user"
|
||||
},
|
||||
"add-project": {
|
||||
"description": "Project to add to user $ID managed array",
|
||||
"type": "string",
|
||||
"example": "abcd100"
|
||||
},
|
||||
"remove-project": {
|
||||
"description": "Project to remove from user $ID managed array",
|
||||
"type": "string",
|
||||
"example": "abcd100"
|
||||
}
|
||||
}
|
||||
},
|
||||
"api.DefaultAPIResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
||||
@@ -79,6 +79,8 @@ func (api *RestAPI) MountAPIRoutes(r chi.Router) {
|
||||
// REST API Uses TokenAuth
|
||||
// User List
|
||||
r.Get("/users/", api.getUsers)
|
||||
// User Edit
|
||||
r.Post("/user/{id}", api.updateUserByRequest)
|
||||
// Cluster List
|
||||
r.Get("/clusters/", api.getClusters)
|
||||
// Slurm node state
|
||||
@@ -152,7 +154,7 @@ func (api *RestAPI) MountConfigAPIRoutes(r chi.Router) {
|
||||
r.Put("/config/users/", api.createUser)
|
||||
r.Get("/config/users/", api.getUsers)
|
||||
r.Delete("/config/users/", api.deleteUser)
|
||||
r.Post("/config/user/{id}", api.updateUser)
|
||||
r.Post("/config/user/{id}", api.updateUserByForm)
|
||||
r.Post("/config/notice/", api.editNotice)
|
||||
r.Get("/config/taggers/", api.getTaggers)
|
||||
r.Post("/config/taggers/run/", api.runTagger)
|
||||
|
||||
@@ -24,6 +24,14 @@ type APIReturnedUser struct {
|
||||
Projects []string `json:"projects"`
|
||||
}
|
||||
|
||||
// UpdateUserAPIRequest model
|
||||
type UpdateUserAPIRequest struct {
|
||||
NewRole string `json:"add-role" example:"user"` // Role to add to user $ID
|
||||
DelRole string `json:"remove-role" example:"user"` // Role to remove from user $ID
|
||||
NewProj string `json:"add-project" example:"abcd100"` // Project to add to user $ID managed array
|
||||
DelProj string `json:"remove-project" example:"abcd100"` // Project to remove from user $ID managed array
|
||||
}
|
||||
|
||||
// getUsers godoc
|
||||
// @summary Returns a list of users
|
||||
// @tags User
|
||||
@@ -58,22 +66,74 @@ func (api *RestAPI) getUsers(rw http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
// updateUser godoc
|
||||
// updateUserByRequest godoc
|
||||
// @summary Update user roles and projects
|
||||
// @tags User
|
||||
// @description Allows admins to add/remove roles and projects for a user
|
||||
// @produce plain
|
||||
// @param id path string true "Username"
|
||||
// @param add-role formData string false "Role to add"
|
||||
// @param remove-role formData string false "Role to remove"
|
||||
// @param add-project formData string false "Project to add"
|
||||
// @param remove-project formData string false "Project to remove"
|
||||
// @accept json
|
||||
// @produce json
|
||||
// @param id path string true "Username"
|
||||
// @param request body api.UpdateUserAPIRequest true "Single Field Changes"
|
||||
// @success 200 {string} string "Success message"
|
||||
// @failure 403 {object} api.ErrorResponse "Forbidden"
|
||||
// @failure 422 {object} api.ErrorResponse "Unprocessable Entity"
|
||||
// @security ApiKeyAuth
|
||||
// @router /api/user/{id} [post]
|
||||
func (api *RestAPI) updateUser(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *RestAPI) updateUserByRequest(rw http.ResponseWriter, r *http.Request) {
|
||||
|
||||
if user := repository.GetUserFromContext(r.Context()); !user.HasRole(schema.RoleAdmin) {
|
||||
handleError(fmt.Errorf("only admins are allowed to update a user"), http.StatusForbidden, rw)
|
||||
return
|
||||
}
|
||||
|
||||
// Get Values
|
||||
var req UpdateUserAPIRequest
|
||||
if err := decode(r.Body, &req); err != nil {
|
||||
handleError(fmt.Errorf("decoding request failed: %w", err), http.StatusBadRequest, rw)
|
||||
return
|
||||
}
|
||||
|
||||
rw.Header().Set("Content-Type", "application/json")
|
||||
|
||||
// Handle role updates
|
||||
if req.NewRole != "" {
|
||||
if err := repository.GetUserRepository().AddRole(r.Context(), chi.URLParam(r, "id"), req.NewRole); err != nil {
|
||||
handleError(fmt.Errorf("adding role failed: %w", err), http.StatusUnprocessableEntity, rw)
|
||||
return
|
||||
}
|
||||
if err := json.NewEncoder(rw).Encode(DefaultAPIResponse{Message: fmt.Sprintf("Add Role Success for user %s", chi.URLParam(r, "id"))}); err != nil {
|
||||
cclog.Errorf("Failed to encode response: %v", err)
|
||||
}
|
||||
} else if req.DelRole != "" {
|
||||
if err := repository.GetUserRepository().RemoveRole(r.Context(), chi.URLParam(r, "id"), req.DelRole); err != nil {
|
||||
handleError(fmt.Errorf("removing role failed: %w", err), http.StatusUnprocessableEntity, rw)
|
||||
return
|
||||
}
|
||||
if err := json.NewEncoder(rw).Encode(DefaultAPIResponse{Message: fmt.Sprintf("Remove Role Success for user %s", chi.URLParam(r, "id"))}); err != nil {
|
||||
cclog.Errorf("Failed to encode response: %v", err)
|
||||
}
|
||||
} else if req.NewProj != "" {
|
||||
if err := repository.GetUserRepository().AddProject(r.Context(), chi.URLParam(r, "id"), req.NewProj); err != nil {
|
||||
handleError(fmt.Errorf("adding project failed: %w", err), http.StatusUnprocessableEntity, rw)
|
||||
return
|
||||
}
|
||||
if err := json.NewEncoder(rw).Encode(DefaultAPIResponse{Message: fmt.Sprintf("Add Project Success for user %s", chi.URLParam(r, "id"))}); err != nil {
|
||||
cclog.Errorf("Failed to encode response: %v", err)
|
||||
}
|
||||
} else if req.DelProj != "" {
|
||||
if err := repository.GetUserRepository().RemoveProject(r.Context(), chi.URLParam(r, "id"), req.DelProj); err != nil {
|
||||
handleError(fmt.Errorf("removing project failed: %w", err), http.StatusUnprocessableEntity, rw)
|
||||
return
|
||||
}
|
||||
if err := json.NewEncoder(rw).Encode(DefaultAPIResponse{Message: fmt.Sprintf("Remove Project Success for user %s", chi.URLParam(r, "id"))}); err != nil {
|
||||
cclog.Errorf("Failed to encode response: %v", err)
|
||||
}
|
||||
} else {
|
||||
handleError(fmt.Errorf("no operation specified: must provide add-role, remove-role, add-project, or remove-project"), http.StatusBadRequest, rw)
|
||||
}
|
||||
}
|
||||
|
||||
func (api *RestAPI) updateUserByForm(rw http.ResponseWriter, r *http.Request) {
|
||||
// SecuredCheck() only worked with TokenAuth: Removed
|
||||
|
||||
if user := repository.GetUserFromContext(r.Context()); !user.HasRole(schema.RoleAdmin) {
|
||||
|
||||
@@ -632,7 +632,7 @@ func securedCheck(user *schema.User, r *http.Request) error {
|
||||
}
|
||||
// If SplitHostPort fails, IPAddress is already just a host (no port)
|
||||
|
||||
// If nothing declared in config: Continue
|
||||
// If nothing declared in config: Continue // FIXME: Allow All If Not Declared?
|
||||
if len(config.Keys.APIAllowedIPs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user